From a4b800731622fb83a648a02b7fc39a13f3c02aa2 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 21 Jun 2024 21:52:08 +0000 Subject: [PATCH] decider --- .github/workflows/e2e.yml | 5 + .github/workflows/linter.yml | 12 + .gitignore | 3 +- README.md | 2 +- config/config.go | 2 + go.mod | 13 +- go.sum | 26 +- main/main.go | 42 +++- messages/teleporter/message_handler.go | 58 ++++- messages/teleporter/message_handler_test.go | 1 + proto/README.md | 11 + proto/buf.gen.yaml | 8 + proto/buf.lock | 8 + proto/buf.yaml | 8 + proto/decider/v1/decider.proto | 21 ++ proto/pb/decider/v1/decider.pb.go | 261 ++++++++++++++++++++ proto/pb/decider/v1/decider_grpc.pb.go | 109 ++++++++ relayer/listener.go | 3 + scripts/build.sh | 2 + scripts/e2e_test.sh | 1 + scripts/lint.sh | 2 + scripts/protobuf_codegen.sh | 56 +++++ scripts/test.sh | 5 +- tests/cmd/decider/main.go | 49 ++++ tests/e2e_test.go | 30 ++- tests/utils/utils.go | 7 +- 26 files changed, 716 insertions(+), 29 deletions(-) create mode 100644 proto/README.md create mode 100644 proto/buf.gen.yaml create mode 100644 proto/buf.lock create mode 100644 proto/buf.yaml create mode 100644 proto/decider/v1/decider.proto create mode 100644 proto/pb/decider/v1/decider.pb.go create mode 100644 proto/pb/decider/v1/decider_grpc.pb.go create mode 100755 scripts/protobuf_codegen.sh create mode 100644 tests/cmd/decider/main.go diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 229d674a..1b89a884 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -50,5 +50,10 @@ jobs: with: submodules: recursive + - name: Install buf + uses: bufbuild/buf-setup-action@v1.31.0 + with: + github_token: ${{ github.token }} + - name: Run E2E Tests run: AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego DATA_DIR=/tmp/e2e-test/data ./scripts/e2e_test.sh diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 512d5df6..9c99f558 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -32,5 +32,17 @@ jobs: with: go-version: ${{ env.GO_VERSION }} + - name: Install buf + uses: bufbuild/buf-setup-action@v1.31.0 + with: + github_token: ${{ github.token }} + - name: Run Lint run: ./scripts/lint.sh --go-lint + + - name: Ensure protobuf changes are checked in + run: | + scripts/protobuf_codegen.sh + git update-index --really-refresh >> /dev/null + git diff-index HEAD # to show the differences + git diff-index --quiet HEAD || (echo 'protobuf generated code changes have not all been checked in' && exit 1) diff --git a/.gitignore b/.gitignore index 003022bb..40211110 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ build/ __debug_bin +tests/cmd/decider/decider .vscode* @@ -12,4 +13,4 @@ server.log # Foundry outputs cache/ -out/ \ No newline at end of file +out/ diff --git a/README.md b/README.md index 64a0380a..5d04bdc3 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ awm-relayer --help Display awm-relayer usag ### Building -Before building, be sure to install Go, which is required even if you're just building the Docker image. +Before building, be sure to install Go, which is required even if you're just building the Docker image. You'll also need to install [buf](github.com/bufbuild/buf/). Build the relayer by running the script: diff --git a/config/config.go b/config/config.go index 4ae1fe00..c9e65108 100644 --- a/config/config.go +++ b/config/config.go @@ -59,6 +59,8 @@ type Config struct { DestinationBlockchains []*DestinationBlockchain `mapstructure:"destination-blockchains" json:"destination-blockchains"` ProcessMissedBlocks bool `mapstructure:"process-missed-blocks" json:"process-missed-blocks"` ManualWarpMessages []*ManualWarpMessage `mapstructure:"manual-warp-messages" json:"manual-warp-messages"` + DeciderHost string `mapstructure:"decider-host" json:"decider-host"` + DeciderPort *uint16 `mapstructure:"decider-port" json:"decider-port"` // convenience field to fetch a blockchain's subnet ID blockchainIDToSubnetID map[ids.ID]ids.ID diff --git a/go.mod b/go.mod index 79d7de99..12d511ed 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.27.9 github.com/aws/aws-sdk-go-v2/service/kms v1.32.1 github.com/ethereum/go-ethereum v1.12.0 + github.com/golang/protobuf v1.5.4 github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 github.com/pkg/errors v0.9.1 @@ -22,6 +23,7 @@ require ( github.com/stretchr/testify v1.9.0 go.uber.org/mock v0.4.0 go.uber.org/zap v1.27.0 + google.golang.org/grpc v1.64.0 ) require ( @@ -85,9 +87,8 @@ require ( golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/tools v0.21.0 // indirect - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) @@ -108,10 +109,9 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect @@ -154,8 +154,7 @@ require ( golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/grpc v1.58.3 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/protobuf v1.33.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8daf95ad..7e23c770 100644 --- a/go.sum +++ b/go.sum @@ -273,8 +273,8 @@ github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoB github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -301,8 +301,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -347,8 +347,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -1011,12 +1011,10 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= -google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1039,8 +1037,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/main/main.go b/main/main.go index e33c5d8e..40a0db56 100644 --- a/main/main.go +++ b/main/main.go @@ -10,6 +10,8 @@ import ( "log" "net/http" "os" + "runtime" + "strconv" "strings" "github.com/alexliesenfeld/health" @@ -33,9 +35,16 @@ import ( "go.uber.org/atomic" "go.uber.org/zap" "golang.org/x/sync/errgroup" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials/insecure" ) -var version = "v0.0.0-dev" +var ( + version = "v0.0.0-dev" + + grpcClient *grpc.ClientConn // for connecting to the decider service +) func main() { fs := config.BuildFlagSet() @@ -232,8 +241,34 @@ func main() { manualWarpMessages[sourceBlockchainID] = append(manualWarpMessages[sourceBlockchainID], &warpLogInfo) } - // Create listeners for each of the subnets configured as a source errGroup, ctx := errgroup.WithContext(context.Background()) + + if cfg.DeciderPort != nil { + port := strconv.FormatUint(uint64(*cfg.DeciderPort), 10) + + host := cfg.DeciderHost + if len(host) == 0 { + host = "localhost" + } + + grpcClient, err = grpc.NewClient( + strings.Join([]string{host, port}, ":"), + grpc.WithTransportCredentials( + insecure.NewCredentials(), + ), + ) + if err != nil { + logger.Fatal( + "Failed to instantiate decider client", + zap.Error(err), + ) + panic(err) + } + runtime.SetFinalizer(grpcClient, func(c *grpc.ClientConn) { c.Close() }) + grpcClient.WaitForStateChange(ctx, connectivity.Ready) + } + + // Create listeners for each of the subnets configured as a source for _, s := range cfg.SourceBlockchains { blockchainID, err := ids.FromString(s.BlockchainID) if err != nil { @@ -302,6 +337,7 @@ func main() { manualWarpMessages[blockchainID], &cfg, ethClient, + grpcClient, applicationRelayers, minHeight, ) @@ -324,6 +360,7 @@ func runListener( manualWarpMessages []*relayerTypes.WarpMessageInfo, globalConfig *config.Config, ethClient ethclient.Client, + grpcClient *grpc.ClientConn, applicationRelayers map[common.Hash]*relayer.ApplicationRelayer, minHeight uint64, ) error { @@ -336,6 +373,7 @@ func runListener( applicationRelayers, minHeight, ethClient, + grpcClient, ) if err != nil { return fmt.Errorf("failed to create listener instance: %w", err) diff --git a/messages/teleporter/message_handler.go b/messages/teleporter/message_handler.go index 806d254a..812e7eff 100644 --- a/messages/teleporter/message_handler.go +++ b/messages/teleporter/message_handler.go @@ -5,8 +5,10 @@ package teleporter import ( "context" + "encoding/hex" "encoding/json" "fmt" + "reflect" "time" "github.com/ava-labs/avalanchego/ids" @@ -15,6 +17,7 @@ import ( warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/messages" + pbDecider "github.com/ava-labs/awm-relayer/proto/pb/decider/v1" "github.com/ava-labs/awm-relayer/utils" "github.com/ava-labs/awm-relayer/vms" "github.com/ava-labs/subnet-evm/accounts/abi/bind" @@ -25,12 +28,14 @@ import ( teleporterUtils "github.com/ava-labs/teleporter/utils/teleporter-utils" "github.com/ethereum/go-ethereum/common" "go.uber.org/zap" + "google.golang.org/grpc" ) type factory struct { messageConfig Config protocolAddress common.Address logger logging.Logger + deciderClient pbDecider.DeciderServiceClient } type messageHandler struct { @@ -38,12 +43,14 @@ type messageHandler struct { teleporterMessage *teleportermessenger.TeleporterMessage unsignedMessage *warp.UnsignedMessage factory *factory + deciderClient pbDecider.DeciderServiceClient } func NewMessageHandlerFactory( logger logging.Logger, messageProtocolAddress common.Address, messageProtocolConfig config.MessageProtocolConfig, + grpcClient *grpc.ClientConn, ) (messages.MessageHandlerFactory, error) { // Marshal the map and unmarshal into the Teleporter config data, err := json.Marshal(messageProtocolConfig.Settings) @@ -65,10 +72,18 @@ func NewMessageHandlerFactory( return nil, err } + var deciderClient pbDecider.DeciderServiceClient + if grpcClient == nil { + deciderClient = nil + } else { + deciderClient = pbDecider.NewDeciderServiceClient(grpcClient) + } + return &factory{ messageConfig: messageConfig, protocolAddress: messageProtocolAddress, logger: logger, + deciderClient: deciderClient, }, nil } @@ -86,6 +101,7 @@ func (f *factory) NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (mess teleporterMessage: teleporterMessage, unsignedMessage: unsignedMessage, factory: f, + deciderClient: f.deciderClient, }, nil } @@ -167,7 +183,47 @@ func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClie return false, nil } - return true, nil + var decision bool = true + deciderClientValue := reflect.ValueOf(m.deciderClient) + if deciderClientValue.IsValid() && !deciderClientValue.IsNil() { + warpMsgIDStr := m.unsignedMessage.ID().Hex() + + warpMsgID, err := hex.DecodeString(warpMsgIDStr) + if err != nil { + m.logger.Error( + "Error decoding message ID", + zap.String("warpMsgIDStr", warpMsgIDStr), + zap.Error(err), + ) + return decision, err + } + + // TODO: add a timeout to the context + response, err := m.deciderClient.ShouldSendMessage( + context.Background(), + &pbDecider.ShouldSendMessageRequest{ + NetworkId: m.unsignedMessage.NetworkID, + SourceChainId: m.unsignedMessage.SourceChainID[:], + Payload: m.unsignedMessage.Payload, + BytesRepresentation: m.unsignedMessage.Bytes(), + Id: warpMsgID, + }, + ) + if err != nil { + m.logger.Error( + "Error response from decider.", + zap.String("destinationBlockchainID", destinationBlockchainID.String()), + zap.String("teleporterMessageID", teleporterMessageID.String()), + zap.Any("warpMessageID", warpMsgIDStr), + zap.Error(err), + ) + return decision, err + } + + decision = response.ShouldSendMessage + } + + return decision, nil } // SendMessage extracts the gasLimit and packs the call data to call the receiveCrossChainMessage method of the Teleporter contract, diff --git a/messages/teleporter/message_handler_test.go b/messages/teleporter/message_handler_test.go index 00557b49..73ae035d 100644 --- a/messages/teleporter/message_handler_test.go +++ b/messages/teleporter/message_handler_test.go @@ -170,6 +170,7 @@ func TestShouldSendMessage(t *testing.T) { logger, messageProtocolAddress, messageProtocolConfig, + nil, ) require.NoError(t, err) messageHandler, err := factory.NewMessageHandler(test.warpUnsignedMessage) diff --git a/proto/README.md b/proto/README.md new file mode 100644 index 00000000..acfc4c10 --- /dev/null +++ b/proto/README.md @@ -0,0 +1,11 @@ +Protobuf linting and generation for this project is managed by +[buf](https://github.com/bufbuild/buf). + +Please find installation instructions on +[https://docs.buf.build/installation/](https://docs.buf.build/installation/). + +Any changes made to proto definition can be updated by running +`protobuf_codegen.sh` located in the `scripts/` directory of this repo. + +Introduction to `buf` +[https://docs.buf.build/tour/introduction](https://docs.buf.build/tour/introduction) diff --git a/proto/buf.gen.yaml b/proto/buf.gen.yaml new file mode 100644 index 00000000..046f039c --- /dev/null +++ b/proto/buf.gen.yaml @@ -0,0 +1,8 @@ +version: v1 +plugins: + - name: go + out: pb + opt: paths=source_relative + - name: go-grpc + out: pb + opt: paths=source_relative diff --git a/proto/buf.lock b/proto/buf.lock new file mode 100644 index 00000000..bb25989f --- /dev/null +++ b/proto/buf.lock @@ -0,0 +1,8 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: prometheus + repository: client-model + commit: e171c0b235c546d5a9a597c2961bd357 + digest: shake256:7db3f73ac0f1dce71e70f304f318e9741e857fd78b7b42f0df7a3da353fbb2f387899da7b0a77ac9ee9565194510e39a913cdb9a8ab3c2ff4b8713428c795213 diff --git a/proto/buf.yaml b/proto/buf.yaml new file mode 100644 index 00000000..fdbbe0fc --- /dev/null +++ b/proto/buf.yaml @@ -0,0 +1,8 @@ +version: v1 +name: buf.build/ava-labs/awm-relayer +breaking: + use: + - FILE +lint: + use: + - DEFAULT diff --git a/proto/decider/v1/decider.proto b/proto/decider/v1/decider.proto new file mode 100644 index 00000000..b63dcedc --- /dev/null +++ b/proto/decider/v1/decider.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package decider.v1; + +option go_package = "github.com/ava-labs/awm-relayer/proto/pb/decider"; + +service DeciderService { + rpc ShouldSendMessage(ShouldSendMessageRequest) returns (ShouldSendMessageResponse); +} + +message ShouldSendMessageRequest { + uint32 network_id = 1; + bytes source_chain_id = 2; + bytes payload = 3; + bytes bytes_representation = 4; + bytes id = 5; +} + +message ShouldSendMessageResponse { + bool should_send_message = 1; +} diff --git a/proto/pb/decider/v1/decider.pb.go b/proto/pb/decider/v1/decider.pb.go new file mode 100644 index 00000000..2fdd34e5 --- /dev/null +++ b/proto/pb/decider/v1/decider.pb.go @@ -0,0 +1,261 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc (unknown) +// source: decider/v1/decider.proto + +package decider + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ShouldSendMessageRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NetworkId uint32 `protobuf:"varint,1,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` + SourceChainId []byte `protobuf:"bytes,2,opt,name=source_chain_id,json=sourceChainId,proto3" json:"source_chain_id,omitempty"` + Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + BytesRepresentation []byte `protobuf:"bytes,4,opt,name=bytes_representation,json=bytesRepresentation,proto3" json:"bytes_representation,omitempty"` + Id []byte `protobuf:"bytes,5,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ShouldSendMessageRequest) Reset() { + *x = ShouldSendMessageRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_decider_v1_decider_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ShouldSendMessageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShouldSendMessageRequest) ProtoMessage() {} + +func (x *ShouldSendMessageRequest) ProtoReflect() protoreflect.Message { + mi := &file_decider_v1_decider_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShouldSendMessageRequest.ProtoReflect.Descriptor instead. +func (*ShouldSendMessageRequest) Descriptor() ([]byte, []int) { + return file_decider_v1_decider_proto_rawDescGZIP(), []int{0} +} + +func (x *ShouldSendMessageRequest) GetNetworkId() uint32 { + if x != nil { + return x.NetworkId + } + return 0 +} + +func (x *ShouldSendMessageRequest) GetSourceChainId() []byte { + if x != nil { + return x.SourceChainId + } + return nil +} + +func (x *ShouldSendMessageRequest) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +func (x *ShouldSendMessageRequest) GetBytesRepresentation() []byte { + if x != nil { + return x.BytesRepresentation + } + return nil +} + +func (x *ShouldSendMessageRequest) GetId() []byte { + if x != nil { + return x.Id + } + return nil +} + +type ShouldSendMessageResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ShouldSendMessage bool `protobuf:"varint,1,opt,name=should_send_message,json=shouldSendMessage,proto3" json:"should_send_message,omitempty"` +} + +func (x *ShouldSendMessageResponse) Reset() { + *x = ShouldSendMessageResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_decider_v1_decider_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ShouldSendMessageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShouldSendMessageResponse) ProtoMessage() {} + +func (x *ShouldSendMessageResponse) ProtoReflect() protoreflect.Message { + mi := &file_decider_v1_decider_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ShouldSendMessageResponse.ProtoReflect.Descriptor instead. +func (*ShouldSendMessageResponse) Descriptor() ([]byte, []int) { + return file_decider_v1_decider_proto_rawDescGZIP(), []int{1} +} + +func (x *ShouldSendMessageResponse) GetShouldSendMessage() bool { + if x != nil { + return x.ShouldSendMessage + } + return false +} + +var File_decider_v1_decider_proto protoreflect.FileDescriptor + +var file_decider_v1_decider_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x64, 0x65, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, 0x63, + 0x69, 0x64, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x64, 0x65, 0x63, 0x69, + 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x22, 0xbe, 0x01, 0x0a, 0x18, 0x53, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x72, 0x65, + 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x13, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, + 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4b, 0x0a, 0x19, 0x53, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x73, + 0x65, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x11, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x32, 0x72, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x72, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x60, 0x0a, 0x11, 0x53, 0x68, 0x6f, 0x75, 0x6c, 0x64, + 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x2e, 0x64, 0x65, + 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x53, + 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x76, 0x61, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, + 0x61, 0x77, 0x6d, 0x2d, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x70, 0x62, 0x2f, 0x64, 0x65, 0x63, 0x69, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_decider_v1_decider_proto_rawDescOnce sync.Once + file_decider_v1_decider_proto_rawDescData = file_decider_v1_decider_proto_rawDesc +) + +func file_decider_v1_decider_proto_rawDescGZIP() []byte { + file_decider_v1_decider_proto_rawDescOnce.Do(func() { + file_decider_v1_decider_proto_rawDescData = protoimpl.X.CompressGZIP(file_decider_v1_decider_proto_rawDescData) + }) + return file_decider_v1_decider_proto_rawDescData +} + +var file_decider_v1_decider_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_decider_v1_decider_proto_goTypes = []interface{}{ + (*ShouldSendMessageRequest)(nil), // 0: decider.v1.ShouldSendMessageRequest + (*ShouldSendMessageResponse)(nil), // 1: decider.v1.ShouldSendMessageResponse +} +var file_decider_v1_decider_proto_depIdxs = []int32{ + 0, // 0: decider.v1.DeciderService.ShouldSendMessage:input_type -> decider.v1.ShouldSendMessageRequest + 1, // 1: decider.v1.DeciderService.ShouldSendMessage:output_type -> decider.v1.ShouldSendMessageResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_decider_v1_decider_proto_init() } +func file_decider_v1_decider_proto_init() { + if File_decider_v1_decider_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_decider_v1_decider_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShouldSendMessageRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_decider_v1_decider_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ShouldSendMessageResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_decider_v1_decider_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_decider_v1_decider_proto_goTypes, + DependencyIndexes: file_decider_v1_decider_proto_depIdxs, + MessageInfos: file_decider_v1_decider_proto_msgTypes, + }.Build() + File_decider_v1_decider_proto = out.File + file_decider_v1_decider_proto_rawDesc = nil + file_decider_v1_decider_proto_goTypes = nil + file_decider_v1_decider_proto_depIdxs = nil +} diff --git a/proto/pb/decider/v1/decider_grpc.pb.go b/proto/pb/decider/v1/decider_grpc.pb.go new file mode 100644 index 00000000..00232b5f --- /dev/null +++ b/proto/pb/decider/v1/decider_grpc.pb.go @@ -0,0 +1,109 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: decider/v1/decider.proto + +package decider + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + DeciderService_ShouldSendMessage_FullMethodName = "/decider.v1.DeciderService/ShouldSendMessage" +) + +// DeciderServiceClient is the client API for DeciderService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type DeciderServiceClient interface { + ShouldSendMessage(ctx context.Context, in *ShouldSendMessageRequest, opts ...grpc.CallOption) (*ShouldSendMessageResponse, error) +} + +type deciderServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewDeciderServiceClient(cc grpc.ClientConnInterface) DeciderServiceClient { + return &deciderServiceClient{cc} +} + +func (c *deciderServiceClient) ShouldSendMessage(ctx context.Context, in *ShouldSendMessageRequest, opts ...grpc.CallOption) (*ShouldSendMessageResponse, error) { + out := new(ShouldSendMessageResponse) + err := c.cc.Invoke(ctx, DeciderService_ShouldSendMessage_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DeciderServiceServer is the server API for DeciderService service. +// All implementations must embed UnimplementedDeciderServiceServer +// for forward compatibility +type DeciderServiceServer interface { + ShouldSendMessage(context.Context, *ShouldSendMessageRequest) (*ShouldSendMessageResponse, error) + mustEmbedUnimplementedDeciderServiceServer() +} + +// UnimplementedDeciderServiceServer must be embedded to have forward compatible implementations. +type UnimplementedDeciderServiceServer struct { +} + +func (UnimplementedDeciderServiceServer) ShouldSendMessage(context.Context, *ShouldSendMessageRequest) (*ShouldSendMessageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ShouldSendMessage not implemented") +} +func (UnimplementedDeciderServiceServer) mustEmbedUnimplementedDeciderServiceServer() {} + +// UnsafeDeciderServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DeciderServiceServer will +// result in compilation errors. +type UnsafeDeciderServiceServer interface { + mustEmbedUnimplementedDeciderServiceServer() +} + +func RegisterDeciderServiceServer(s grpc.ServiceRegistrar, srv DeciderServiceServer) { + s.RegisterService(&DeciderService_ServiceDesc, srv) +} + +func _DeciderService_ShouldSendMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ShouldSendMessageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeciderServiceServer).ShouldSendMessage(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeciderService_ShouldSendMessage_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeciderServiceServer).ShouldSendMessage(ctx, req.(*ShouldSendMessageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// DeciderService_ServiceDesc is the grpc.ServiceDesc for DeciderService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var DeciderService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "decider.v1.DeciderService", + HandlerType: (*DeciderServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ShouldSendMessage", + Handler: _DeciderService_ShouldSendMessage_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "decider/v1/decider.proto", +} diff --git a/relayer/listener.go b/relayer/listener.go index 345e79b9..8cb3f594 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "go.uber.org/atomic" "go.uber.org/zap" + "google.golang.org/grpc" ) const ( @@ -57,6 +58,7 @@ func NewListener( applicationRelayers map[common.Hash]*ApplicationRelayer, startingHeight uint64, ethClient ethclient.Client, + grpcClient *grpc.ClientConn, ) (*Listener, error) { blockchainID, err := ids.FromString(sourceBlockchain.BlockchainID) if err != nil { @@ -97,6 +99,7 @@ func NewListener( logger, address, cfg, + grpcClient, ) case config.OFF_CHAIN_REGISTRY: m, err = offchainregistry.NewMessageHandlerFactory( diff --git a/scripts/build.sh b/scripts/build.sh index c09c205b..d4661991 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -39,6 +39,8 @@ if version_lt "$(go_version)" "$go_version_minimum"; then exit 1 fi +scripts/protobuf_codegen.sh + if [[ $# -eq 1 ]]; then binary_path=$1 elif [[ $# -eq 0 ]]; then diff --git a/scripts/e2e_test.sh b/scripts/e2e_test.sh index f6016ac8..84f6718d 100755 --- a/scripts/e2e_test.sh +++ b/scripts/e2e_test.sh @@ -66,6 +66,7 @@ source "$RELAYER_PATH"/scripts/versions.sh go install -v github.com/onsi/ginkgo/v2/ginkgo@${GINKGO_VERSION} ginkgo build ./tests/ +go build -v -o tests/cmd/decider/decider ./tests/cmd/decider/ # Run the tests echo "Running e2e tests $RUN_E2E" diff --git a/scripts/lint.sh b/scripts/lint.sh index 49a2282a..7321e833 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -15,3 +15,5 @@ source $RELAYER_PATH/scripts/versions.sh go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION} golangci-lint run --config=$RELAYER_PATH/.golangci.yml --build-tags=testing ./... --timeout 5m + +(cd proto && buf lint) diff --git a/scripts/protobuf_codegen.sh b/scripts/protobuf_codegen.sh new file mode 100755 index 00000000..d5786892 --- /dev/null +++ b/scripts/protobuf_codegen.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if ! [[ "$0" =~ scripts/protobuf_codegen.sh ]]; then + echo "must be run from repository root" + exit 255 +fi + +## ensure the correct version of "buf" is installed +BUF_VERSION='1.31.0' +if [[ $(buf --version | cut -f2 -d' ') != "${BUF_VERSION}" ]]; then + echo "could not find buf ${BUF_VERSION}, is it installed + in PATH?" + exit 255 +fi + +## install "protoc-gen-go" +PROTOC_GEN_GO_VERSION='v1.33.0' +go install -v google.golang.org/protobuf/cmd/protoc-gen-go@${PROTOC_GEN_GO_VERSION} +if [[ $(protoc-gen-go --version | cut -f2 -d' ') != "${PROTOC_GEN_GO_VERSION}" ]]; then + # e.g., protoc-gen-go v1.28.1 + echo "could not find protoc-gen-go ${PROTOC_GEN_GO_VERSION}, is it installed + in PATH?" + exit 255 +fi + +### install "protoc-gen-go-grpc" +PROTOC_GEN_GO_GRPC_VERSION='1.3.0' +go install -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@v${PROTOC_GEN_GO_GRPC_VERSION} +if [[ $(protoc-gen-go-grpc --version | cut -f2 -d' ') != "${PROTOC_GEN_GO_GRPC_VERSION}" ]]; then + # e.g., protoc-gen-go-grpc 1.3.0 + echo "could not find protoc-gen-go-grpc ${PROTOC_GEN_GO_GRPC_VERSION}, is it installed + in PATH?" + exit 255 +fi + +TARGET=$PWD/proto +if [ -n "${1:-}" ]; then + TARGET="$1" +fi + +# move to api directory +cd "$TARGET" + +echo "Running protobuf fmt..." +buf format -w + +echo "Running protobuf lint check..." +if ! buf lint; then + echo "ERROR: protobuf linter failed" + exit 1 +fi + +echo "Re-generating protobuf..." +if ! buf generate; then + echo "ERROR: protobuf generation failed" + exit 1 +fi diff --git a/scripts/test.sh b/scripts/test.sh index d453f813..a25ba856 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -32,4 +32,7 @@ RELAYER_PATH=$( cd .. && pwd ) source "$RELAYER_PATH"/scripts/constants.sh -go test -tags testing $VERBOSE ./... \ No newline at end of file + +go build -o tests/cmd/decider/decider ./tests/cmd/decider/ + +go test -tags testing $VERBOSE ./... diff --git a/tests/cmd/decider/main.go b/tests/cmd/decider/main.go new file mode 100644 index 00000000..2589ca91 --- /dev/null +++ b/tests/cmd/decider/main.go @@ -0,0 +1,49 @@ +// a CLI command to serve as a gRPC provider of awm-relayer/proto/decider + +package main + +import ( + "context" + "flag" + "fmt" + "log" + "net" + + pb "github.com/ava-labs/awm-relayer/proto/pb/decider/v1" + "google.golang.org/grpc" +) + +var port = flag.Int("port", 50051, "The server port") + +type deciderServer struct { + pb.UnimplementedDeciderServiceServer +} + +func (s *deciderServer) ShouldSendMessage( + ctx context.Context, + msg *pb.ShouldSendMessageRequest, +) (*pb.ShouldSendMessageResponse, error) { + return &pb.ShouldSendMessageResponse{ + ShouldSendMessage: true, + }, nil +} + +func main() { + flag.Parse() + + server := grpc.NewServer() + + pb.RegisterDeciderServiceServer(server, &deciderServer{}) + + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) + if err != nil { + log.Fatalf("decider failed to listen: %v", err) + } + + log.Printf("decider listening at %v", listener.Addr()) + + err = server.Serve(listener) + if err != nil { + log.Fatalf("decider failed to serve: %v", err) + } +} diff --git a/tests/e2e_test.go b/tests/e2e_test.go index a587c874..8c1bd68f 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -4,8 +4,12 @@ package tests import ( + "context" "encoding/hex" "os" + "os/exec" + "os/signal" + "syscall" "testing" testUtils "github.com/ava-labs/awm-relayer/tests/utils" @@ -23,6 +27,9 @@ const ( var ( localNetworkInstance *local.LocalNetwork + + decider *exec.Cmd + cancelDecider context.CancelFunc ) func TestE2E(t *testing.T) { @@ -36,6 +43,7 @@ func TestE2E(t *testing.T) { // Define the Relayer before and after suite functions. var _ = ginkgo.BeforeSuite(func() { + localNetworkInstance = local.NewLocalNetwork(warpGenesisFile) // Generate the Teleporter deployment values teleporterContractAddress := common.HexToAddress(testUtils.ReadHexTextFile("./tests/utils/UniversalTeleporterMessengerContractAddress.txt")) @@ -54,11 +62,31 @@ var _ = ginkgo.BeforeSuite(func() { ) log.Info("Deployed Teleporter contracts") localNetworkInstance.DeployTeleporterRegistryContracts(teleporterContractAddress, fundedKey) + + var ctx context.Context + ctx, cancelDecider = context.WithCancel(context.Background()) + // we'll call cancelDecider in AfterSuite, but also call it if this + // process is killed, because AfterSuite won't always run then: + signalChan := make(chan os.Signal, 2) + signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) + go func() { + <-signalChan + cancelDecider() + }() + decider = exec.CommandContext(ctx, "./tests/cmd/decider/decider") + decider.Start() + log.Info("Started decider service") + log.Info("Set up ginkgo before suite") }) var _ = ginkgo.AfterSuite(func() { - localNetworkInstance.TearDownNetwork() + if localNetworkInstance != nil { + localNetworkInstance.TearDownNetwork() + } + if decider != nil { + cancelDecider() + } }) var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { diff --git a/tests/utils/utils.go b/tests/utils/utils.go index 3d8fd8d1..b66f4fd2 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -1,7 +1,7 @@ // Copyright (C) 2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package tests +package utils import ( "bufio" @@ -95,6 +95,7 @@ func BuildAndRunRelayerExecutable(ctx context.Context, relayerConfigPath string) panic(fmt.Errorf("relayer exited abnormally: %w", err)) } }() + return func() { relayerCancel() <-relayerContext.Done() @@ -190,6 +191,8 @@ func CreateDefaultRelayerConfig( ) } + var deciderPort uint16 = 50051 + return config.Config{ LogLevel: logging.Info.LowerString(), PChainAPI: &config.APIConfig{ @@ -204,6 +207,8 @@ func CreateDefaultRelayerConfig( MetricsPort: 9090, SourceBlockchains: sources, DestinationBlockchains: destinations, + DeciderHost: "localhost", + DeciderPort: &deciderPort, } }