From 315049b698868b58486a01f330ad3e36af0b109d Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 7 Jun 2024 10:09:33 -0400 Subject: [PATCH 01/70] Refactor application relayers --- main/main.go | 291 +++++++++++++++-------------- relayer/listener.go | 332 ++++++++------------------------- relayer/message_coordinator.go | 232 +++++++++++++++++++++++ types/types.go | 3 +- vms/destination_client.go | 4 +- 5 files changed, 459 insertions(+), 403 deletions(-) create mode 100644 relayer/message_coordinator.go diff --git a/main/main.go b/main/main.go index 58995590..d478356e 100644 --- a/main/main.go +++ b/main/main.go @@ -11,6 +11,10 @@ import ( "net/http" "os" + "github.com/ava-labs/awm-relayer/messages" + offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" + "github.com/ava-labs/awm-relayer/messages/teleporter" + "github.com/alexliesenfeld/health" "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/avalanchego/ids" @@ -101,18 +105,14 @@ func main() { logger.Info("Initializing destination clients") destinationClients, err := vms.CreateDestinationClients(logger, cfg) if err != nil { - logger.Error( - "Failed to create destination clients", - zap.Error(err), - ) + logger.Fatal("Failed to create destination clients", zap.Error(err)) panic(err) } // Initialize metrics gathered through prometheus gatherer, registerer, err := initializeMetrics() if err != nil { - logger.Fatal("Failed to set up prometheus metrics", - zap.Error(err)) + logger.Fatal("Failed to set up prometheus metrics", zap.Error(err)) panic(err) } @@ -131,10 +131,7 @@ func main() { &cfg, ) if err != nil { - logger.Error( - "Failed to create app request network", - zap.Error(err), - ) + logger.Fatal("Failed to create app request network", zap.Error(err)) panic(err) } @@ -170,32 +167,23 @@ func main() { startMetricsServer(logger, gatherer, cfg.MetricsPort) - metrics, err := relayer.NewApplicationRelayerMetrics(registerer) + relayerMetrics, err := relayer.NewApplicationRelayerMetrics(registerer) if err != nil { - logger.Error( - "Failed to create application relayer metrics", - zap.Error(err), - ) + logger.Fatal("Failed to create application relayer metrics", zap.Error(err)) panic(err) } // Initialize message creator passed down to relayers for creating app requests. messageCreator, err := message.NewCreator(logger, registerer, "message_creator", constants.DefaultNetworkCompressionType, constants.DefaultNetworkMaximumInboundTimeout) if err != nil { - logger.Error( - "Failed to create message creator", - zap.Error(err), - ) + logger.Fatal("Failed to create message creator", zap.Error(err)) panic(err) } // Initialize the database db, err := database.NewDatabase(logger, &cfg) if err != nil { - logger.Error( - "Failed to create database", - zap.Error(err), - ) + logger.Fatal("Failed to create database", zap.Error(err)) panic(err) } @@ -203,13 +191,32 @@ func main() { ticker := utils.NewTicker(cfg.DBWriteIntervalSeconds) go ticker.Run() + messageHandlerFactories, err := createMessageHandlerFactories(logger, &cfg) + if err != nil { + logger.Fatal("Failed to create Message Handler Factories", zap.Error(err)) + panic(err) + } + + applicationRelayers, minHeights, err := createApplicationRelayers( + context.Background(), + logger, + relayerMetrics, + db, + ticker, + network, + messageCreator, + &cfg, + destinationClients, + ) + relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers) + // Gather manual Warp messages specified in the configuration manualWarpMessages := make(map[ids.ID][]*relayerTypes.WarpMessageInfo) for _, msg := range cfg.ManualWarpMessages { sourceBlockchainID := msg.GetSourceBlockchainID() unsignedMsg, err := types.UnpackWarpMessage(msg.GetUnsignedMessageBytes()) if err != nil { - logger.Error( + logger.Fatal( "Failed to unpack manual Warp message", zap.String("warpMessageBytes", hex.EncodeToString(msg.GetUnsignedMessageBytes())), zap.Error(err), @@ -226,136 +233,147 @@ func main() { // Create listeners for each of the subnets configured as a source errGroup, ctx := errgroup.WithContext(context.Background()) for _, s := range cfg.SourceBlockchains { - blockchainID, err := ids.FromString(s.BlockchainID) - if err != nil { - logger.Error( - "Invalid subnetID in configuration", - zap.Error(err), - ) - panic(err) - } sourceBlockchain := s - health := atomic.NewBool(true) - relayerHealth[blockchainID] = health + isHealthy := atomic.NewBool(true) + relayerHealth[s.GetBlockchainID()] = isHealthy - // errgroup will cancel the context when the first goroutine returns an error errGroup.Go(func() error { - // Dial the eth client - ethClient, err := ethclient.DialWithConfig( - context.Background(), - sourceBlockchain.RPCEndpoint.BaseURL, - sourceBlockchain.RPCEndpoint.HTTPHeaders, - sourceBlockchain.RPCEndpoint.QueryParams, - ) - if err != nil { - logger.Error( - "Failed to connect to node via RPC", - zap.String("blockchainID", sourceBlockchain.BlockchainID), - zap.Error(err), - ) - return err - } - - // Create the ApplicationRelayers - applicationRelayers, minHeight, err := createApplicationRelayers( - ctx, - logger, - metrics, - db, - ticker, - *sourceBlockchain, - network, - messageCreator, - &cfg, - ethClient, - destinationClients, - ) - if err != nil { - logger.Error( - "Failed to create application relayers", - zap.String("blockchainID", sourceBlockchain.BlockchainID), - zap.Error(err), - ) - return err - } - logger.Info( - "Created application relayers", - zap.String("blockchainID", sourceBlockchain.BlockchainID), - ) + return relayer.GetMessageCoordinator().ProcessManualWarpMessages(logger, manualWarpMessages[sourceBlockchain.GetBlockchainID()], *sourceBlockchain) + }) + // errgroup will cancel the context when the first goroutine returns an error + errGroup.Go(func() error { // runListener runs until it errors or the context is cancelled by another goroutine - return runListener( + return relayer.RunListener( ctx, logger, *sourceBlockchain, - health, - manualWarpMessages[blockchainID], - &cfg, - ethClient, - applicationRelayers, - minHeight, + isHealthy, + cfg.ProcessMissedBlocks, + minHeights[sourceBlockchain.GetBlockchainID()], ) }) } err = errGroup.Wait() - logger.Error( - "Relayer exiting.", - zap.Error(err), - ) + logger.Error("Relayer exiting.", zap.Error(err)) } -// runListener creates a Listener instance and the ApplicationRelayers for a subnet. -// The Listener listens for warp messages on that subnet, and the ApplicationRelayers handle delivery to the destination -func runListener( - ctx context.Context, +func createMessageHandlerFactories( logger logging.Logger, - sourceBlockchain config.SourceBlockchain, - relayerHealth *atomic.Bool, - manualWarpMessages []*relayerTypes.WarpMessageInfo, globalConfig *config.Config, - ethClient ethclient.Client, - applicationRelayers map[common.Hash]*relayer.ApplicationRelayer, - minHeight uint64, -) error { - // Create the Listener - listener, err := relayer.NewListener( - logger, - sourceBlockchain, - relayerHealth, - globalConfig, - applicationRelayers, - minHeight, - ethClient, - ) - if err != nil { - return fmt.Errorf("failed to create listener instance: %w", err) +) (map[ids.ID]map[common.Address]messages.MessageHandlerFactory, error) { + messageHandlerFactories := make(map[ids.ID]map[common.Address]messages.MessageHandlerFactory) + for _, sourceBlockchain := range globalConfig.SourceBlockchains { + messageHandlerFactoriesForSource := make(map[common.Address]messages.MessageHandlerFactory) + // Create message managers for each supported message protocol + for addressStr, cfg := range sourceBlockchain.MessageContracts { + address := common.HexToAddress(addressStr) + format := cfg.MessageFormat + var ( + m messages.MessageHandlerFactory + err error + ) + switch config.ParseMessageProtocol(format) { + case config.TELEPORTER: + m, err = teleporter.NewMessageHandlerFactory( + logger, + address, + cfg, + ) + case config.OFF_CHAIN_REGISTRY: + m, err = offchainregistry.NewMessageHandlerFactory( + logger, + cfg, + ) + default: + m, err = nil, fmt.Errorf("invalid message format %s", format) + } + if err != nil { + logger.Error("Failed to create message manager", zap.Error(err)) + return nil, err + } + messageHandlerFactoriesForSource[address] = m + } + messageHandlerFactories[sourceBlockchain.GetBlockchainID()] = messageHandlerFactoriesForSource } - logger.Info( - "Created listener", - zap.String("blockchainID", sourceBlockchain.BlockchainID), - ) - err = listener.ProcessManualWarpMessages(logger, manualWarpMessages, sourceBlockchain) - if err != nil { - logger.Error( - "Failed to process manual Warp messages", - zap.String("blockchainID", sourceBlockchain.BlockchainID), - zap.Error(err), + return messageHandlerFactories, nil +} + +func createApplicationRelayers( + ctx context.Context, + logger logging.Logger, + relayerMetrics *relayer.ApplicationRelayerMetrics, + db database.RelayerDatabase, + ticker *utils.Ticker, + network *peers.AppRequestNetwork, + messageCreator message.Creator, + cfg *config.Config, + destinationClients map[ids.ID]vms.DestinationClient, +) (map[common.Hash]*relayer.ApplicationRelayer, map[ids.ID]uint64, error) { + applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) + minHeights := make(map[ids.ID]uint64) + for _, sourceBlockchain := range cfg.SourceBlockchains { + ethClient, err := ethclient.DialWithConfig( + ctx, + sourceBlockchain.RPCEndpoint.BaseURL, + sourceBlockchain.RPCEndpoint.HTTPHeaders, + sourceBlockchain.RPCEndpoint.QueryParams, ) - } + if err != nil { + logger.Error( + "Failed to connect to node via RPC", + zap.String("blockchainID", sourceBlockchain.BlockchainID), + zap.Error(err), + ) + return nil, nil, err + } - logger.Info( - "Listener initialized. Listening for messages to relay.", - zap.String("originBlockchainID", sourceBlockchain.BlockchainID), - ) + currentHeight, err := ethClient.BlockNumber(ctx) + if err != nil { + logger.Error("Failed to get current block height", zap.Error(err)) + return nil, nil, err + } + ethClient.Close() + + // Create the ApplicationRelayers + applicationRelayersForSource, minHeight, err := createApplicationRelayersForSourceChain( + ctx, + logger, + relayerMetrics, + db, + ticker, + *sourceBlockchain, + network, + messageCreator, + cfg, + currentHeight, + destinationClients, + ) + if err != nil { + logger.Error( + "Failed to create application relayers", + zap.String("blockchainID", sourceBlockchain.BlockchainID), + zap.Error(err), + ) + return nil, nil, err + } - // Wait for logs from the subscribed node - // Will only return on error or context cancellation - return listener.ProcessLogs(ctx) + for relayerID, applicationRelayer := range applicationRelayersForSource { + applicationRelayers[relayerID] = applicationRelayer + } + minHeights[sourceBlockchain.GetBlockchainID()] = minHeight + + logger.Info( + "Created application relayers", + zap.String("blockchainID", sourceBlockchain.BlockchainID), + ) + } + return applicationRelayers, minHeights, nil } // createApplicationRelayers creates Application Relayers for a given source blockchain. -func createApplicationRelayers( +func createApplicationRelayersForSourceChain( ctx context.Context, logger logging.Logger, metrics *relayer.ApplicationRelayerMetrics, @@ -365,7 +383,7 @@ func createApplicationRelayers( network *peers.AppRequestNetwork, messageCreator message.Creator, cfg *config.Config, - srcEthClient ethclient.Client, + currentHeight uint64, destinationClients map[ids.ID]vms.DestinationClient, ) (map[common.Hash]*relayer.ApplicationRelayer, uint64, error) { // Create the ApplicationRelayers @@ -375,17 +393,8 @@ func createApplicationRelayers( ) applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) - currentHeight, err := srcEthClient.BlockNumber(context.Background()) - if err != nil { - logger.Error( - "Failed to get current block height", - zap.Error(err), - ) - return nil, 0, err - } - // Each ApplicationRelayer determines its starting height based on the database state. - // The Listener begins processing messages starting from the minimum height across all of the ApplicationRelayers + // The Listener begins processing messages starting from the minimum height across all the ApplicationRelayers minHeight := uint64(0) for _, relayerID := range database.GetSourceBlockchainRelayerIDs(&sourceBlockchain) { height, err := database.CalculateStartingBlockHeight( diff --git a/relayer/listener.go b/relayer/listener.go index 052d6f06..f9e0177d 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -8,19 +8,13 @@ import ( "fmt" "math/big" "math/rand" - "sync" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/awm-relayer/config" - "github.com/ava-labs/awm-relayer/database" "github.com/ava-labs/awm-relayer/ethclient" - "github.com/ava-labs/awm-relayer/messages" - offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" - "github.com/ava-labs/awm-relayer/messages/teleporter" relayerTypes "github.com/ava-labs/awm-relayer/types" - vms "github.com/ava-labs/awm-relayer/vms" - "github.com/ethereum/go-ethereum/common" + "github.com/ava-labs/awm-relayer/vms" "go.uber.org/atomic" "go.uber.org/zap" ) @@ -34,28 +28,57 @@ const ( // Listener handles all messages sent from a given source chain type Listener struct { - Subscriber vms.Subscriber - requestIDLock *sync.Mutex - currentRequestID uint32 - contractMessage vms.ContractMessage - messageHandlerFactories map[common.Address]messages.MessageHandlerFactory - logger logging.Logger - sourceBlockchain config.SourceBlockchain - catchUpResultChan chan bool - healthStatus *atomic.Bool - globalConfig *config.Config - applicationRelayers map[common.Hash]*ApplicationRelayer - ethClient ethclient.Client + Subscriber vms.Subscriber + currentRequestID uint32 + contractMessage vms.ContractMessage + logger logging.Logger + sourceBlockchain config.SourceBlockchain + catchUpResultChan chan bool + healthStatus *atomic.Bool + processMissedBlocks bool + ethClient ethclient.Client } -func NewListener( +// runListener creates a Listener instance and the ApplicationRelayers for a subnet. +// The Listener listens for warp messages on that subnet, and the ApplicationRelayers handle delivery to the destination +func RunListener( + ctx context.Context, logger logging.Logger, sourceBlockchain config.SourceBlockchain, relayerHealth *atomic.Bool, - globalConfig *config.Config, - applicationRelayers map[common.Hash]*ApplicationRelayer, + processMissedBlocks bool, + minHeight uint64, +) error { + // Create the Listener + listener, err := newListener( + ctx, + logger, + sourceBlockchain, + relayerHealth, + processMissedBlocks, + minHeight, + ) + if err != nil { + return fmt.Errorf("failed to create listener instance: %w", err) + } + + logger.Info( + "Listener initialized. Listening for messages to relay.", + zap.String("originBlockchainID", sourceBlockchain.BlockchainID), + ) + + // Wait for logs from the subscribed node + // Will only return on error or context cancellation + return listener.ProcessLogs(ctx) +} + +func newListener( + ctx context.Context, + logger logging.Logger, + sourceBlockchain config.SourceBlockchain, + relayerHealth *atomic.Bool, + processMissedBlocks bool, startingHeight uint64, - ethClient ethclient.Client, ) (*Listener, error) { blockchainID, err := ids.FromString(sourceBlockchain.BlockchainID) if err != nil { @@ -66,7 +89,7 @@ func NewListener( return nil, err } ethWSClient, err := ethclient.DialWithConfig( - context.Background(), + ctx, sourceBlockchain.WSEndpoint.BaseURL, sourceBlockchain.WSEndpoint.HTTPHeaders, sourceBlockchain.WSEndpoint.QueryParams, @@ -81,40 +104,6 @@ func NewListener( } sub := vms.NewSubscriber(logger, config.ParseVM(sourceBlockchain.VM), blockchainID, ethWSClient) - // Create message managers for each supported message protocol - messageHandlerFactories := make(map[common.Address]messages.MessageHandlerFactory) - for addressStr, cfg := range sourceBlockchain.MessageContracts { - address := common.HexToAddress(addressStr) - format := cfg.MessageFormat - var ( - m messages.MessageHandlerFactory - err error - ) - switch config.ParseMessageProtocol(format) { - case config.TELEPORTER: - m, err = teleporter.NewMessageHandlerFactory( - logger, - address, - cfg, - ) - case config.OFF_CHAIN_REGISTRY: - m, err = offchainregistry.NewMessageHandlerFactory( - logger, - cfg, - ) - default: - m, err = nil, fmt.Errorf("invalid message format %s", format) - } - if err != nil { - logger.Error( - "Failed to create message manager", - zap.Error(err), - ) - return nil, err - } - messageHandlerFactories[address] = m - } - // Marks when the listener has finished the catch-up process on startup. // Until that time, we do not know the order in which messages are processed, // since the catch-up process occurs concurrently with normal message processing @@ -123,6 +112,22 @@ func NewListener( // scenario. catchUpResultChan := make(chan bool, 1) + // Dial the eth client + ethRPCClient, err := ethclient.DialWithConfig( + ctx, + sourceBlockchain.RPCEndpoint.BaseURL, + sourceBlockchain.RPCEndpoint.HTTPHeaders, + sourceBlockchain.RPCEndpoint.QueryParams, + ) + if err != nil { + logger.Error( + "Failed to connect to node via RPC", + zap.String("blockchainID", blockchainID.String()), + zap.Error(err), + ) + return nil, err + } + logger.Info( "Creating relayer", zap.String("subnetID", sourceBlockchain.GetSubnetID().String()), @@ -131,18 +136,15 @@ func NewListener( zap.String("blockchainIDHex", sourceBlockchain.GetBlockchainID().Hex()), ) lstnr := Listener{ - Subscriber: sub, - requestIDLock: &sync.Mutex{}, - currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision - contractMessage: vms.NewContractMessage(logger, sourceBlockchain), - messageHandlerFactories: messageHandlerFactories, - logger: logger, - sourceBlockchain: sourceBlockchain, - catchUpResultChan: catchUpResultChan, - healthStatus: relayerHealth, - globalConfig: globalConfig, - applicationRelayers: applicationRelayers, - ethClient: ethClient, + Subscriber: sub, + currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision + contractMessage: vms.NewContractMessage(logger, sourceBlockchain), + logger: logger, + sourceBlockchain: sourceBlockchain, + catchUpResultChan: catchUpResultChan, + healthStatus: relayerHealth, + processMissedBlocks: processMissedBlocks, + ethClient: ethRPCClient, } // Open the subscription. We must do this before processing any missed messages, otherwise we may miss an incoming message @@ -156,7 +158,7 @@ func NewListener( return nil, err } - if lstnr.globalConfig.ProcessMissedBlocks { + if lstnr.processMissedBlocks { // Process historical blocks in a separate goroutine so that the main processing loop can // start processing new blocks as soon as possible. Otherwise, it's possible for // ProcessFromHeight to overload the message queue and cause a deadlock. @@ -228,34 +230,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { zap.Uint64("blockNumber", block.BlockNumber), ) - // Register each message in the block with the appropriate application relayer - messageHandlers := make(map[common.Hash][]messages.MessageHandler) - for _, warpLogInfo := range block.Messages { - appRelayer, handler, err := lstnr.GetAppRelayerMessageHandler(warpLogInfo) - if err != nil { - lstnr.logger.Error( - "Failed to parse message", - zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - zap.Error(err), - ) - continue - } - if appRelayer == nil { - lstnr.logger.Debug("Application relayer not found. Skipping message relay") - continue - } - messageHandlers[appRelayer.relayerID.ID] = append(messageHandlers[appRelayer.relayerID.ID], handler) - } - // Initiate message relay of all registered messages - for _, appRelayer := range lstnr.applicationRelayers { - // Dispatch all messages in the block to the appropriate application relayer. - // An empty slice is still a valid argument to ProcessHeight; in this case the height is immediately committed. - handlers := messageHandlers[appRelayer.relayerID.ID] - - // Process the height async. This is safe because the ApplicationRelayer maintains the threadsafe - // invariant that heights are committed to the database one at a time, in order, with no gaps. - go appRelayer.ProcessHeight(block.BlockNumber, handlers, errChan) - } + go GetMessageCoordinator().ProcessWarpBlock(block, errChan) case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) lstnr.logger.Error( @@ -297,162 +272,3 @@ func (lstnr *Listener) reconnectToSubscriber() error { lstnr.healthStatus.Store(true) return nil } - -// Unpacks the Warp message and fetches the appropriate application relayer -// Checks for the following registered keys. At most one of these keys should be registered. -// 1. An exact match on sourceBlockchainID, destinationBlockchainID, originSenderAddress, and destinationAddress -// 2. A match on sourceBlockchainID and destinationBlockchainID, with a specific originSenderAddress and any destinationAddress -// 3. A match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and a specific destinationAddress -// 4. A match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and any destinationAddress -func (lstnr *Listener) getApplicationRelayer( - sourceBlockchainID ids.ID, - originSenderAddress common.Address, - destinationBlockchainID ids.ID, - destinationAddress common.Address, -) *ApplicationRelayer { - // Check for an exact match - applicationRelayerID := database.CalculateRelayerID( - sourceBlockchainID, - destinationBlockchainID, - originSenderAddress, - destinationAddress, - ) - if applicationRelayer, ok := lstnr.applicationRelayers[applicationRelayerID]; ok { - return applicationRelayer - } - - // Check for a match on sourceBlockchainID and destinationBlockchainID, with a specific originSenderAddress and any destinationAddress - applicationRelayerID = database.CalculateRelayerID( - sourceBlockchainID, - destinationBlockchainID, - originSenderAddress, - database.AllAllowedAddress, - ) - if applicationRelayer, ok := lstnr.applicationRelayers[applicationRelayerID]; ok { - return applicationRelayer - } - - // Check for a match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and a specific destinationAddress - applicationRelayerID = database.CalculateRelayerID( - sourceBlockchainID, - destinationBlockchainID, - database.AllAllowedAddress, - destinationAddress, - ) - if applicationRelayer, ok := lstnr.applicationRelayers[applicationRelayerID]; ok { - return applicationRelayer - } - - // Check for a match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and any destinationAddress - applicationRelayerID = database.CalculateRelayerID( - sourceBlockchainID, - destinationBlockchainID, - database.AllAllowedAddress, - database.AllAllowedAddress, - ) - if applicationRelayer, ok := lstnr.applicationRelayers[applicationRelayerID]; ok { - return applicationRelayer - } - lstnr.logger.Debug( - "Application relayer not found. Skipping message relay.", - zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - zap.String("destinationBlockchainID", destinationBlockchainID.String()), - zap.String("originSenderAddress", originSenderAddress.String()), - zap.String("destinationAddress", destinationAddress.String()), - ) - return nil -} - -// Returns the ApplicationRelayer that is configured to handle this message, as well as a one-time MessageHandler -// instance that the ApplicationRelayer uses to relay this specific message. -// The MessageHandler and ApplicationRelayer are decoupled to support batch workflows in which a single ApplicationRelayer -// processes multiple messages (using their corresponding MessageHandlers) in a single shot. -func (lstnr *Listener) GetAppRelayerMessageHandler(warpMessageInfo *relayerTypes.WarpMessageInfo) ( - *ApplicationRelayer, - messages.MessageHandler, - error, -) { - // Check that the warp message is from a supported message protocol contract address. - messageHandlerFactory, supportedMessageProtocol := lstnr.messageHandlerFactories[warpMessageInfo.SourceAddress] - if !supportedMessageProtocol { - // Do not return an error here because it is expected for there to be messages from other contracts - // than just the ones supported by a single listener instance. - lstnr.logger.Debug( - "Warp message from unsupported message protocol address. Not relaying.", - zap.String("protocolAddress", warpMessageInfo.SourceAddress.Hex()), - ) - return nil, nil, nil - } - messageHandler, err := messageHandlerFactory.NewMessageHandler(warpMessageInfo.UnsignedMessage) - if err != nil { - lstnr.logger.Error( - "Failed to create message handler", - zap.Error(err), - ) - return nil, nil, err - } - - // Fetch the message delivery data - sourceBlockchainID, originSenderAddress, destinationBlockchainID, destinationAddress, err := messageHandler.GetMessageRoutingInfo() - if err != nil { - lstnr.logger.Error( - "Failed to get message routing information", - zap.Error(err), - ) - return nil, nil, err - } - - lstnr.logger.Info( - "Unpacked warp message", - zap.String("sourceBlockchainID", sourceBlockchainID.String()), - zap.String("originSenderAddress", originSenderAddress.String()), - zap.String("destinationBlockchainID", destinationBlockchainID.String()), - zap.String("destinationAddress", destinationAddress.String()), - zap.String("warpMessageID", warpMessageInfo.UnsignedMessage.ID().String()), - ) - - appRelayer := lstnr.getApplicationRelayer( - sourceBlockchainID, - originSenderAddress, - destinationBlockchainID, - destinationAddress, - ) - if appRelayer == nil { - return nil, nil, nil - } - return appRelayer, messageHandler, nil -} - -func (lstnr *Listener) ProcessManualWarpMessages( - logger logging.Logger, - manualWarpMessages []*relayerTypes.WarpMessageInfo, - sourceBlockchain config.SourceBlockchain, -) error { - // Send any messages that were specified in the configuration - for _, warpMessage := range manualWarpMessages { - logger.Info( - "Relaying manual Warp message", - zap.String("blockchainID", sourceBlockchain.BlockchainID), - zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), - ) - appRelayer, handler, err := lstnr.GetAppRelayerMessageHandler(warpMessage) - if err != nil { - logger.Error( - "Failed to parse manual Warp message.", - zap.Error(err), - zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), - ) - return err - } - err = appRelayer.ProcessMessage(handler) - if err != nil { - logger.Error( - "Failed to process manual Warp message", - zap.String("blockchainID", sourceBlockchain.BlockchainID), - zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), - ) - return err - } - } - return nil -} diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go new file mode 100644 index 00000000..1eb2602f --- /dev/null +++ b/relayer/message_coordinator.go @@ -0,0 +1,232 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package relayer + +import ( + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/awm-relayer/config" + "github.com/ava-labs/awm-relayer/database" + "github.com/ava-labs/awm-relayer/messages" + relayerTypes "github.com/ava-labs/awm-relayer/types" + "github.com/ethereum/go-ethereum/common" + "go.uber.org/zap" +) + +var globalMessageCoordinator *MessageCoordinator + +type MessageCoordinator struct { + logger logging.Logger + // Maps Source chain ID and protocol address to a Message Handler Factory + MessageHandlerFactories map[ids.ID]map[common.Address]messages.MessageHandlerFactory + ApplicationRelayers map[common.Hash]*ApplicationRelayer +} + +func SetMessageCoordinator( + logger logging.Logger, + messageHandlerFactories map[ids.ID]map[common.Address]messages.MessageHandlerFactory, + applicationRelayers map[common.Hash]*ApplicationRelayer, +) { + globalMessageCoordinator = &MessageCoordinator{ + logger: logger, + MessageHandlerFactories: messageHandlerFactories, + ApplicationRelayers: applicationRelayers, + } +} + +func GetMessageCoordinator() *MessageCoordinator { + return globalMessageCoordinator +} + +// GetAppRelayerMessageHandler Returns the ApplicationRelayer that is configured to handle this message, as well as a +// one-time MessageHandler instance that the ApplicationRelayer uses to relay this specific message. +// The MessageHandler and ApplicationRelayer are decoupled to support batch workflows in which a single ApplicationRelayer +// processes multiple messages (using their corresponding MessageHandlers) in a single shot. +func (mc *MessageCoordinator) GetAppRelayerMessageHandler( + warpMessageInfo *relayerTypes.WarpMessageInfo, +) ( + *ApplicationRelayer, + messages.MessageHandler, + error, +) { + // Check that the warp message is from a supported message protocol contract address. + messageHandlerFactory, supportedMessageProtocol := mc.MessageHandlerFactories[warpMessageInfo.UnsignedMessage.SourceChainID][warpMessageInfo.SourceAddress] + if !supportedMessageProtocol { + // Do not return an error here because it is expected for there to be messages from other contracts + // than just the ones supported by a single listener instance. + mc.logger.Debug( + "Warp message from unsupported message protocol address. Not relaying.", + zap.String("protocolAddress", warpMessageInfo.SourceAddress.Hex()), + ) + return nil, nil, nil + } + messageHandler, err := messageHandlerFactory.NewMessageHandler(warpMessageInfo.UnsignedMessage) + if err != nil { + mc.logger.Error( + "Failed to create message handler", + zap.Error(err), + ) + return nil, nil, err + } + + // Fetch the message delivery data + sourceBlockchainID, originSenderAddress, destinationBlockchainID, destinationAddress, err := messageHandler.GetMessageRoutingInfo() + if err != nil { + mc.logger.Error( + "Failed to get message routing information", + zap.Error(err), + ) + return nil, nil, err + } + + mc.logger.Info( + "Unpacked warp message", + zap.String("sourceBlockchainID", sourceBlockchainID.String()), + zap.String("originSenderAddress", originSenderAddress.String()), + zap.String("destinationBlockchainID", destinationBlockchainID.String()), + zap.String("destinationAddress", destinationAddress.String()), + zap.String("warpMessageID", warpMessageInfo.UnsignedMessage.ID().String()), + ) + + appRelayer := mc.getApplicationRelayer( + sourceBlockchainID, + originSenderAddress, + destinationBlockchainID, + destinationAddress, + ) + if appRelayer == nil { + return nil, nil, nil + } + return appRelayer, messageHandler, nil +} + +// Unpacks the Warp message and fetches the appropriate application relayer +// Checks for the following registered keys. At most one of these keys should be registered. +// 1. An exact match on sourceBlockchainID, destinationBlockchainID, originSenderAddress, and destinationAddress +// 2. A match on sourceBlockchainID and destinationBlockchainID, with a specific originSenderAddress and any destinationAddress +// 3. A match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and a specific destinationAddress +// 4. A match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and any destinationAddress +func (mc *MessageCoordinator) getApplicationRelayer( + sourceBlockchainID ids.ID, + originSenderAddress common.Address, + destinationBlockchainID ids.ID, + destinationAddress common.Address, +) *ApplicationRelayer { + // Check for an exact match + applicationRelayerID := database.CalculateRelayerID( + sourceBlockchainID, + destinationBlockchainID, + originSenderAddress, + destinationAddress, + ) + if applicationRelayer, ok := mc.ApplicationRelayers[applicationRelayerID]; ok { + return applicationRelayer + } + + // Check for a match on sourceBlockchainID and destinationBlockchainID, with a specific originSenderAddress and any destinationAddress + applicationRelayerID = database.CalculateRelayerID( + sourceBlockchainID, + destinationBlockchainID, + originSenderAddress, + database.AllAllowedAddress, + ) + if applicationRelayer, ok := mc.ApplicationRelayers[applicationRelayerID]; ok { + return applicationRelayer + } + + // Check for a match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and a specific destinationAddress + applicationRelayerID = database.CalculateRelayerID( + sourceBlockchainID, + destinationBlockchainID, + database.AllAllowedAddress, + destinationAddress, + ) + if applicationRelayer, ok := mc.ApplicationRelayers[applicationRelayerID]; ok { + return applicationRelayer + } + + // Check for a match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and any destinationAddress + applicationRelayerID = database.CalculateRelayerID( + sourceBlockchainID, + destinationBlockchainID, + database.AllAllowedAddress, + database.AllAllowedAddress, + ) + if applicationRelayer, ok := mc.ApplicationRelayers[applicationRelayerID]; ok { + return applicationRelayer + } + mc.logger.Debug( + "Application relayer not found. Skipping message relay.", + zap.String("blockchainID", sourceBlockchainID.String()), + zap.String("destinationBlockchainID", destinationBlockchainID.String()), + zap.String("originSenderAddress", originSenderAddress.String()), + zap.String("destinationAddress", destinationAddress.String()), + ) + return nil +} + +func (mc *MessageCoordinator) ProcessManualWarpMessages( + logger logging.Logger, + manualWarpMessages []*relayerTypes.WarpMessageInfo, + sourceBlockchain config.SourceBlockchain, +) error { + // Send any messages that were specified in the configuration + for _, warpMessage := range manualWarpMessages { + logger.Info( + "Relaying manual Warp message", + zap.String("blockchainID", sourceBlockchain.BlockchainID), + zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), + ) + appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) + if err != nil { + logger.Error( + "Failed to parse manual Warp message.", + zap.Error(err), + zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), + ) + return err + } + err = appRelayer.ProcessMessage(handler) + if err != nil { + logger.Error( + "Failed to process manual Warp message", + zap.String("blockchainID", sourceBlockchain.BlockchainID), + zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), + ) + return err + } + } + return nil +} + +func (mc *MessageCoordinator) ProcessWarpBlock(block *relayerTypes.WarpBlockInfo, errChan chan error) { + // Register each message in the block with the appropriate application relayer + messageHandlers := make(map[common.Hash][]messages.MessageHandler) + for _, warpLogInfo := range block.Messages { + appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpLogInfo) + if err != nil { + mc.logger.Error( + "Failed to parse message", + zap.String("blockchainID", warpLogInfo.UnsignedMessage.SourceChainID.String()), + zap.Error(err), + ) + continue + } + if appRelayer == nil { + mc.logger.Debug("Application relayer not found. Skipping message relay") + continue + } + messageHandlers[appRelayer.relayerID.ID] = append(messageHandlers[appRelayer.relayerID.ID], handler) + } + // Initiate message relay of all registered messages + for _, appRelayer := range mc.ApplicationRelayers { + // Dispatch all messages in the block to the appropriate application relayer. + // An empty slice is still a valid argument to ProcessHeight; in this case the height is immediately committed. + handlers := messageHandlers[appRelayer.relayerID.ID] + + // Process the height async. This is safe because the ApplicationRelayer maintains the threadsafe + // invariant that heights are committed to the database one at a time, in order, with no gaps. + go appRelayer.ProcessHeight(block.BlockNumber, handlers, errChan) + } +} diff --git a/types/types.go b/types/types.go index 094a1629..210f0e32 100644 --- a/types/types.go +++ b/types/types.go @@ -20,8 +20,7 @@ var WarpPrecompileLogFilter = warp.WarpABI.Events["SendWarpMessage"].ID var ErrInvalidLog = errors.New("invalid warp message log") // WarpBlockInfo describes the block height and logs needed to process Warp messages. -// WarpBlockInfo instances are populated by the subscriber, and forwared to the -// Listener to process +// WarpBlockInfo instances are populated by the subscriber, and forwarded to the Listener to process. type WarpBlockInfo struct { BlockNumber uint64 Messages []*WarpMessageInfo diff --git a/vms/destination_client.go b/vms/destination_client.go index 8060cbfa..12341e23 100644 --- a/vms/destination_client.go +++ b/vms/destination_client.go @@ -20,7 +20,7 @@ import ( // DestinationClient is the interface for the destination chain client. Methods that interact with the destination chain // should generally be implemented in a thread safe way, as they will be called concurrently by the application relayers. type DestinationClient interface { - // SendTx contructs the transaction from warp primitives, and send to the configured destination chain endpoint + // SendTx constructs the transaction from warp primitives, and send to the configured destination chain endpoint // TODO: Make generic for any VM. SendTx(signedMessage *warp.Message, toAddress string, gasLimit uint64, callData []byte) error @@ -51,7 +51,7 @@ func CreateDestinationClients(logger logging.Logger, relayerConfig config.Config if err != nil { logger.Error( "Failed to decode base-58 encoded source chain ID", - zap.String("blockchainID", blockchainID.String()), + zap.String("blockchainID", subnetInfo.BlockchainID), zap.Error(err), ) return nil, err From 6254a6c46e0ca974e2d2872f2f592489b7a59f7f Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 7 Jun 2024 12:51:12 -0400 Subject: [PATCH 02/70] wip --- main/main.go | 2 +- relayer/listener.go | 6 +++--- relayer/message_coordinator.go | 37 +++++++++++++++++++++------------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/main/main.go b/main/main.go index d478356e..31bbc982 100644 --- a/main/main.go +++ b/main/main.go @@ -239,7 +239,7 @@ func main() { relayerHealth[s.GetBlockchainID()] = isHealthy errGroup.Go(func() error { - return relayer.GetMessageCoordinator().ProcessManualWarpMessages(logger, manualWarpMessages[sourceBlockchain.GetBlockchainID()], *sourceBlockchain) + return relayer.ProcessManualWarpMessages(manualWarpMessages[sourceBlockchain.GetBlockchainID()]) }) // errgroup will cancel the context when the first goroutine returns an error diff --git a/relayer/listener.go b/relayer/listener.go index f9e0177d..a083ac77 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -69,7 +69,7 @@ func RunListener( // Wait for logs from the subscribed node // Will only return on error or context cancellation - return listener.ProcessLogs(ctx) + return listener.processLogs(ctx) } func newListener( @@ -177,7 +177,7 @@ func newListener( // Listens to the Subscriber logs channel to process them. // On subscriber error, attempts to reconnect and errors if unable. // Exits if context is cancelled by another goroutine. -func (lstnr *Listener) ProcessLogs(ctx context.Context) error { +func (lstnr *Listener) processLogs(ctx context.Context) error { // Error channel for application relayer errors errChan := make(chan error) for { @@ -230,7 +230,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { zap.Uint64("blockNumber", block.BlockNumber), ) - go GetMessageCoordinator().ProcessWarpBlock(block, errChan) + go ProcessWarpBlock(block, errChan) case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) lstnr.logger.Error( diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 1eb2602f..6f8fd251 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -4,9 +4,10 @@ package relayer import ( + "fmt" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/database" "github.com/ava-labs/awm-relayer/messages" relayerTypes "github.com/ava-labs/awm-relayer/types" @@ -35,10 +36,6 @@ func SetMessageCoordinator( } } -func GetMessageCoordinator() *MessageCoordinator { - return globalMessageCoordinator -} - // GetAppRelayerMessageHandler Returns the ApplicationRelayer that is configured to handle this message, as well as a // one-time MessageHandler instance that the ApplicationRelayer uses to relay this specific message. // The MessageHandler and ApplicationRelayer are decoupled to support batch workflows in which a single ApplicationRelayer @@ -166,21 +163,26 @@ func (mc *MessageCoordinator) getApplicationRelayer( return nil } -func (mc *MessageCoordinator) ProcessManualWarpMessages( - logger logging.Logger, +func ProcessManualWarpMessages(manualWarpMessages []*relayerTypes.WarpMessageInfo) error { + if globalMessageCoordinator == nil { + return fmt.Errorf("global message coordinator not set") + } + return globalMessageCoordinator.processManualWarpMessages(manualWarpMessages) +} + +func (mc *MessageCoordinator) processManualWarpMessages( manualWarpMessages []*relayerTypes.WarpMessageInfo, - sourceBlockchain config.SourceBlockchain, ) error { // Send any messages that were specified in the configuration for _, warpMessage := range manualWarpMessages { - logger.Info( + mc.logger.Info( "Relaying manual Warp message", - zap.String("blockchainID", sourceBlockchain.BlockchainID), + zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), ) appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) if err != nil { - logger.Error( + mc.logger.Error( "Failed to parse manual Warp message.", zap.Error(err), zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), @@ -189,9 +191,9 @@ func (mc *MessageCoordinator) ProcessManualWarpMessages( } err = appRelayer.ProcessMessage(handler) if err != nil { - logger.Error( + mc.logger.Error( "Failed to process manual Warp message", - zap.String("blockchainID", sourceBlockchain.BlockchainID), + zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), ) return err @@ -200,7 +202,14 @@ func (mc *MessageCoordinator) ProcessManualWarpMessages( return nil } -func (mc *MessageCoordinator) ProcessWarpBlock(block *relayerTypes.WarpBlockInfo, errChan chan error) { +func ProcessWarpBlock(block *relayerTypes.WarpBlockInfo, errChan chan error) { + if globalMessageCoordinator == nil { + panic("global message coordinator not set") + } + globalMessageCoordinator.processWarpBlock(block, errChan) +} + +func (mc *MessageCoordinator) processWarpBlock(block *relayerTypes.WarpBlockInfo, errChan chan error) { // Register each message in the block with the appropriate application relayer messageHandlers := make(map[common.Hash][]messages.MessageHandler) for _, warpLogInfo := range block.Messages { From ccd4ef55f1a0dbe21054aaeaf893b8efe80322a0 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 10 Jun 2024 11:57:39 -0400 Subject: [PATCH 03/70] Functions for relaying message from blockchain ID, warp message ID, and block number --- main/main.go | 51 +++++++++++++++++++-------- relayer/listener.go | 40 +++------------------ relayer/message_coordinator.go | 64 ++++++++++++++++++++++++++-------- types/types.go | 30 ++++++++++++---- 4 files changed, 115 insertions(+), 70 deletions(-) diff --git a/main/main.go b/main/main.go index 31bbc982..ac470333 100644 --- a/main/main.go +++ b/main/main.go @@ -109,6 +109,14 @@ func main() { panic(err) } + // Initialize all source clients + logger.Info("Initializing destination clients") + sourceClients, err := createSourceClients(context.Background(), logger, &cfg) + if err != nil { + logger.Fatal("Failed to create source clients", zap.Error(err)) + panic(err) + } + // Initialize metrics gathered through prometheus gatherer, registerer, err := initializeMetrics() if err != nil { @@ -206,6 +214,7 @@ func main() { network, messageCreator, &cfg, + sourceClients, destinationClients, ) relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers) @@ -249,6 +258,7 @@ func main() { ctx, logger, *sourceBlockchain, + sourceClients[sourceBlockchain.GetBlockchainID()], isHealthy, cfg.ProcessMissedBlocks, minHeights[sourceBlockchain.GetBlockchainID()], @@ -300,21 +310,16 @@ func createMessageHandlerFactories( return messageHandlerFactories, nil } -func createApplicationRelayers( +func createSourceClients( ctx context.Context, logger logging.Logger, - relayerMetrics *relayer.ApplicationRelayerMetrics, - db database.RelayerDatabase, - ticker *utils.Ticker, - network *peers.AppRequestNetwork, - messageCreator message.Creator, cfg *config.Config, - destinationClients map[ids.ID]vms.DestinationClient, -) (map[common.Hash]*relayer.ApplicationRelayer, map[ids.ID]uint64, error) { - applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) - minHeights := make(map[ids.ID]uint64) +) (map[ids.ID]ethclient.Client, error) { + var err error + clients := make(map[ids.ID]ethclient.Client) + for _, sourceBlockchain := range cfg.SourceBlockchains { - ethClient, err := ethclient.DialWithConfig( + clients[sourceBlockchain.GetBlockchainID()], err = ethclient.DialWithConfig( ctx, sourceBlockchain.RPCEndpoint.BaseURL, sourceBlockchain.RPCEndpoint.HTTPHeaders, @@ -326,15 +331,33 @@ func createApplicationRelayers( zap.String("blockchainID", sourceBlockchain.BlockchainID), zap.Error(err), ) - return nil, nil, err + return nil, err } + } + return clients, nil +} - currentHeight, err := ethClient.BlockNumber(ctx) +// Returns a map of application relayers, as well as a map of source blockchain IDs to starting heights. +func createApplicationRelayers( + ctx context.Context, + logger logging.Logger, + relayerMetrics *relayer.ApplicationRelayerMetrics, + db database.RelayerDatabase, + ticker *utils.Ticker, + network *peers.AppRequestNetwork, + messageCreator message.Creator, + cfg *config.Config, + sourceClients map[ids.ID]ethclient.Client, + destinationClients map[ids.ID]vms.DestinationClient, +) (map[common.Hash]*relayer.ApplicationRelayer, map[ids.ID]uint64, error) { + applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) + minHeights := make(map[ids.ID]uint64) + for _, sourceBlockchain := range cfg.SourceBlockchains { + currentHeight, err := sourceClients[sourceBlockchain.GetBlockchainID()].BlockNumber(ctx) if err != nil { logger.Error("Failed to get current block height", zap.Error(err)) return nil, nil, err } - ethClient.Close() // Create the ApplicationRelayers applicationRelayersForSource, minHeight, err := createApplicationRelayersForSourceChain( diff --git a/relayer/listener.go b/relayer/listener.go index a083ac77..07b28d0b 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -13,7 +13,6 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/ethclient" - relayerTypes "github.com/ava-labs/awm-relayer/types" "github.com/ava-labs/awm-relayer/vms" "go.uber.org/atomic" "go.uber.org/zap" @@ -45,6 +44,7 @@ func RunListener( ctx context.Context, logger logging.Logger, sourceBlockchain config.SourceBlockchain, + ethRPCClient ethclient.Client, relayerHealth *atomic.Bool, processMissedBlocks bool, minHeight uint64, @@ -54,6 +54,7 @@ func RunListener( ctx, logger, sourceBlockchain, + ethRPCClient, relayerHealth, processMissedBlocks, minHeight, @@ -76,6 +77,7 @@ func newListener( ctx context.Context, logger logging.Logger, sourceBlockchain config.SourceBlockchain, + ethRPCClient ethclient.Client, relayerHealth *atomic.Bool, processMissedBlocks bool, startingHeight uint64, @@ -112,22 +114,6 @@ func newListener( // scenario. catchUpResultChan := make(chan bool, 1) - // Dial the eth client - ethRPCClient, err := ethclient.DialWithConfig( - ctx, - sourceBlockchain.RPCEndpoint.BaseURL, - sourceBlockchain.RPCEndpoint.HTTPHeaders, - sourceBlockchain.RPCEndpoint.QueryParams, - ) - if err != nil { - logger.Error( - "Failed to connect to node via RPC", - zap.String("blockchainID", blockchainID.String()), - zap.Error(err), - ) - return nil, err - } - logger.Info( "Creating relayer", zap.String("subnetID", sourceBlockchain.GetSubnetID().String()), @@ -212,25 +198,7 @@ func (lstnr *Listener) processLogs(ctx context.Context) error { return fmt.Errorf("failed to catch up on historical blocks") } case blockHeader := <-lstnr.Subscriber.Headers(): - // Parse the logs in the block, and group by application relayer - - block, err := relayerTypes.NewWarpBlockInfo(blockHeader, lstnr.ethClient) - if err != nil { - lstnr.logger.Error( - "Failed to create Warp block info", - zap.Error(err), - ) - continue - } - - // Relay the messages in the block to the destination chains. Continue on failure. - lstnr.logger.Debug( - "Processing block", - zap.String("sourceBlockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - zap.Uint64("blockNumber", block.BlockNumber), - ) - - go ProcessWarpBlock(block, errChan) + go ProcessBlock(blockHeader, lstnr.ethClient, errChan) case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) lstnr.logger.Error( diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 6f8fd251..03c9e503 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -4,13 +4,17 @@ package relayer import ( + "errors" "fmt" + "math/big" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/awm-relayer/database" + "github.com/ava-labs/awm-relayer/ethclient" "github.com/ava-labs/awm-relayer/messages" relayerTypes "github.com/ava-labs/awm-relayer/types" + "github.com/ava-labs/subnet-evm/core/types" "github.com/ethereum/go-ethereum/common" "go.uber.org/zap" ) @@ -19,9 +23,10 @@ var globalMessageCoordinator *MessageCoordinator type MessageCoordinator struct { logger logging.Logger - // Maps Source chain ID and protocol address to a Message Handler Factory + // Maps Source blockchain ID and protocol address to a Message Handler Factory MessageHandlerFactories map[ids.ID]map[common.Address]messages.MessageHandlerFactory ApplicationRelayers map[common.Hash]*ApplicationRelayer + SourceClients map[ids.ID]ethclient.Client } func SetMessageCoordinator( @@ -60,20 +65,14 @@ func (mc *MessageCoordinator) GetAppRelayerMessageHandler( } messageHandler, err := messageHandlerFactory.NewMessageHandler(warpMessageInfo.UnsignedMessage) if err != nil { - mc.logger.Error( - "Failed to create message handler", - zap.Error(err), - ) + mc.logger.Error("Failed to create message handler", zap.Error(err)) return nil, nil, err } // Fetch the message delivery data sourceBlockchainID, originSenderAddress, destinationBlockchainID, destinationAddress, err := messageHandler.GetMessageRoutingInfo() if err != nil { - mc.logger.Error( - "Failed to get message routing information", - zap.Error(err), - ) + mc.logger.Error("Failed to get message routing information", zap.Error(err)) return nil, nil, err } @@ -202,11 +201,50 @@ func (mc *MessageCoordinator) processManualWarpMessages( return nil } -func ProcessWarpBlock(block *relayerTypes.WarpBlockInfo, errChan chan error) { +func ProcessBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { if globalMessageCoordinator == nil { panic("global message coordinator not set") } - globalMessageCoordinator.processWarpBlock(block, errChan) + globalMessageCoordinator.processBlock(blockHeader, ethClient, errChan) +} + +func (mc *MessageCoordinator) ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) error { + ethClient, ok := mc.SourceClients[blockchainID] + if !ok { + return fmt.Errorf("source client not set for blockchain ID: %s", blockchainID.String()) + } + + warpMessage, err := relayerTypes.FetchWarpMessageFromID(ethClient, messageID, blockNum) + if err != nil { + return err + } + + appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) + if err != nil { + mc.logger.Error( + "Failed to parse message", + zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), + zap.Error(err), + ) + return err + } + if appRelayer == nil { + mc.logger.Error("Application relayer not found") + return errors.New("application relayer not found") + } + + return appRelayer.ProcessMessage(handler) +} + +func (mc *MessageCoordinator) processBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { + // Parse the logs in the block, and group by application relayer + block, err := relayerTypes.NewWarpBlockInfo(blockHeader, ethClient) + if err != nil { + mc.logger.Error("Failed to create Warp block info", zap.Error(err)) + return + } + + mc.processWarpBlock(block, errChan) } func (mc *MessageCoordinator) processWarpBlock(block *relayerTypes.WarpBlockInfo, errChan chan error) { @@ -234,8 +272,6 @@ func (mc *MessageCoordinator) processWarpBlock(block *relayerTypes.WarpBlockInfo // An empty slice is still a valid argument to ProcessHeight; in this case the height is immediately committed. handlers := messageHandlers[appRelayer.relayerID.ID] - // Process the height async. This is safe because the ApplicationRelayer maintains the threadsafe - // invariant that heights are committed to the database one at a time, in order, with no gaps. - go appRelayer.ProcessHeight(block.BlockNumber, handlers, errChan) + appRelayer.ProcessHeight(block.BlockNumber, handlers, errChan) } } diff --git a/types/types.go b/types/types.go index 363586c2..03f003fc 100644 --- a/types/types.go +++ b/types/types.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "math/big" "time" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" @@ -49,7 +50,7 @@ func NewWarpBlockInfo(header *types.Header, ethClient ethclient.Client) (*WarpBl ) // Check if the block contains warp logs, and fetch them from the client if it does if header.Bloom.Test(WarpPrecompileLogFilter[:]) { - logs, err = fetchWarpLogsWithRetries(ethClient, header, filterLogsRetries, retryInterval) + logs, err = fetchWarpLogsWithRetries(ethClient, header.Number, filterLogsRetries, retryInterval) if err != nil { return nil, err } @@ -102,9 +103,26 @@ func UnpackWarpMessage(unsignedMsgBytes []byte) (*avalancheWarp.UnsignedMessage, return unsignedMsg, nil } +func FetchWarpMessageFromID(ethClient ethclient.Client, warpMessageID common.Hash, blockNum *big.Int) (*WarpMessageInfo, error) { + logs, err := ethClient.FilterLogs(context.Background(), interfaces.FilterQuery{ + Topics: [][]common.Hash{{WarpPrecompileLogFilter}, nil, {warpMessageID}}, + Addresses: []common.Address{warp.ContractAddress}, + FromBlock: blockNum, + ToBlock: blockNum, + }) + if err != nil { + return nil, err + } + if len(logs) != 1 { + return nil, ErrInvalidLog + } + + return NewWarpMessageInfo(logs[0]) +} + // The node serving the filter logs request may be behind the node serving the block header request, // so we retry a few times to ensure we get the logs -func fetchWarpLogsWithRetries(ethClient ethclient.Client, header *types.Header, numRetries int, retryInterval time.Duration) ([]types.Log, error) { +func fetchWarpLogsWithRetries(ethClient ethclient.Client, blockNum *big.Int, numRetries int, retryInterval time.Duration) ([]types.Log, error) { var ( logs []types.Log err error @@ -112,10 +130,10 @@ func fetchWarpLogsWithRetries(ethClient ethclient.Client, header *types.Header, for i := 0; i < numRetries; i++ { logs, err = ethClient.FilterLogs(context.Background(), interfaces.FilterQuery{ - Topics: [][]common.Hash{{WarpPrecompileLogFilter}}, + Topics: [][]common.Hash{{WarpPrecompileLogFilter}, nil}, Addresses: []common.Address{warp.ContractAddress}, - FromBlock: header.Number, - ToBlock: header.Number, + FromBlock: blockNum, + ToBlock: blockNum, }) if err == nil { return logs, nil @@ -124,5 +142,5 @@ func fetchWarpLogsWithRetries(ethClient ethclient.Client, header *types.Header, time.Sleep(retryInterval) } } - return nil, fmt.Errorf("failed to fetch warp logs for block %d after %d retries: %w", header.Number.Uint64(), filterLogsRetries, err) + return nil, fmt.Errorf("failed to fetch warp logs for block %d after %d retries: %w", blockNum.Uint64(), filterLogsRetries, err) } From a6fb5cfed6c2a8ca099f1d172522dc69e6d8bf36 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 10 Jun 2024 12:04:23 -0400 Subject: [PATCH 04/70] lint --- main/main.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main/main.go b/main/main.go index ac470333..e943489e 100644 --- a/main/main.go +++ b/main/main.go @@ -217,6 +217,10 @@ func main() { sourceClients, destinationClients, ) + if err != nil { + logger.Fatal("Failed to create Application Relayers", zap.Error(err)) + panic(err) + } relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers) // Gather manual Warp messages specified in the configuration From d67e619115dd3b7239dd2ae55f4fee718d9f0efa Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 10 Jun 2024 12:28:27 -0400 Subject: [PATCH 05/70] Fixes --- relayer/message_coordinator.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 03c9e503..c7cee4f1 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -201,17 +201,10 @@ func (mc *MessageCoordinator) processManualWarpMessages( return nil } -func ProcessBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { - if globalMessageCoordinator == nil { - panic("global message coordinator not set") - } - globalMessageCoordinator.processBlock(blockHeader, ethClient, errChan) -} - func (mc *MessageCoordinator) ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) error { ethClient, ok := mc.SourceClients[blockchainID] if !ok { - return fmt.Errorf("source client not set for blockchain ID: %s", blockchainID.String()) + return fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) } warpMessage, err := relayerTypes.FetchWarpMessageFromID(ethClient, messageID, blockNum) @@ -236,18 +229,23 @@ func (mc *MessageCoordinator) ProcessMessage(blockchainID ids.ID, messageID comm return appRelayer.ProcessMessage(handler) } +func ProcessBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { + if globalMessageCoordinator == nil { + panic("global message coordinator not set") + } + globalMessageCoordinator.processBlock(blockHeader, ethClient, errChan) +} + +// Meant to be ran asynchronously. Errors should be sent to errChan. func (mc *MessageCoordinator) processBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { // Parse the logs in the block, and group by application relayer block, err := relayerTypes.NewWarpBlockInfo(blockHeader, ethClient) if err != nil { mc.logger.Error("Failed to create Warp block info", zap.Error(err)) + errChan <- err return } - mc.processWarpBlock(block, errChan) -} - -func (mc *MessageCoordinator) processWarpBlock(block *relayerTypes.WarpBlockInfo, errChan chan error) { // Register each message in the block with the appropriate application relayer messageHandlers := make(map[common.Hash][]messages.MessageHandler) for _, warpLogInfo := range block.Messages { @@ -256,6 +254,7 @@ func (mc *MessageCoordinator) processWarpBlock(block *relayerTypes.WarpBlockInfo mc.logger.Error( "Failed to parse message", zap.String("blockchainID", warpLogInfo.UnsignedMessage.SourceChainID.String()), + zap.String("protocolAddress", warpLogInfo.SourceAddress.String()), zap.Error(err), ) continue @@ -272,6 +271,6 @@ func (mc *MessageCoordinator) processWarpBlock(block *relayerTypes.WarpBlockInfo // An empty slice is still a valid argument to ProcessHeight; in this case the height is immediately committed. handlers := messageHandlers[appRelayer.relayerID.ID] - appRelayer.ProcessHeight(block.BlockNumber, handlers, errChan) + go appRelayer.ProcessHeight(block.BlockNumber, handlers, errChan) } } From 96ecc68ac5da45ab398702b8c9a6af724f974ab2 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 11 Jun 2024 12:14:14 -0400 Subject: [PATCH 06/70] WIP --- main/main.go | 16 ++--- relayer/message_coordinator.go | 9 ++- relayer/relay_message_api_handler.go | 48 +++++++++++++++ tests/basic_relay.go | 6 +- tests/e2e_test.go | 3 + tests/relay_message_api.go | 90 ++++++++++++++++++++++++++++ 6 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 relayer/relay_message_api_handler.go create mode 100644 tests/relay_message_api.go diff --git a/main/main.go b/main/main.go index e943489e..805ce920 100644 --- a/main/main.go +++ b/main/main.go @@ -166,13 +166,6 @@ func main() { }), ) - http.Handle("/health", health.NewHandler(checker)) - - // start the health check server - go func() { - log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%d", cfg.APIPort), nil)) - }() - startMetricsServer(logger, gatherer, cfg.MetricsPort) relayerMetrics, err := relayer.NewApplicationRelayerMetrics(registerer) @@ -223,6 +216,15 @@ func main() { } relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers) + // Initialize the API after the message coordinator is set + http.Handle("/health", health.NewHandler(checker)) + http.Handle("/relay-message", relayer.RelayMessageAPIHandler()) + + // start the health check server + go func() { + log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%d", cfg.APIPort), nil)) + }() + // Gather manual Warp messages specified in the configuration manualWarpMessages := make(map[ids.ID][]*relayerTypes.WarpMessageInfo) for _, msg := range cfg.ManualWarpMessages { diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index c7cee4f1..e27a42bd 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -201,7 +201,14 @@ func (mc *MessageCoordinator) processManualWarpMessages( return nil } -func (mc *MessageCoordinator) ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) error { +func ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) error { + if globalMessageCoordinator == nil { + panic("global message coordinator not set") + } + return globalMessageCoordinator.processMessage(blockchainID, messageID, blockNum) +} + +func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) error { ethClient, ok := mc.SourceClients[blockchainID] if !ok { return fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go new file mode 100644 index 00000000..71cb9f2b --- /dev/null +++ b/relayer/relay_message_api_handler.go @@ -0,0 +1,48 @@ +package relayer + +import ( + "encoding/json" + "math/big" + "net/http" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ethereum/go-ethereum/common" +) + +type RelayMessageRequest struct { + BlockchainID string `json:"blockchain-id"` + MessageID string `json:"message-id"` + BlockNum string `json:"block-num"` +} + +func RelayMessageAPIHandler() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req RelayMessageRequest + + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + blockchainID, err := ids.FromString(req.BlockchainID) + if err != nil { + http.Error(w, "invalid blockchainID"+err.Error(), http.StatusBadRequest) + return + } + messageID := common.HexToHash(req.MessageID) + blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) + if !ok { + http.Error(w, "invalid blockNum", http.StatusBadRequest) + return + } + + err = ProcessMessage(blockchainID, messageID, blockNum) + if err != nil { + http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write([]byte("Message processed successfully")) + } +} diff --git a/tests/basic_relay.go b/tests/basic_relay.go index 1c2a52df..0021a8ba 100644 --- a/tests/basic_relay.go +++ b/tests/basic_relay.go @@ -114,8 +114,10 @@ func BasicRelay(network interfaces.LocalNetwork) { relayerIDA := database.CalculateRelayerID(subnetAInfo.BlockchainID, subnetBInfo.BlockchainID, database.AllAllowedAddress, database.AllAllowedAddress) relayerIDB := database.CalculateRelayerID(subnetBInfo.BlockchainID, subnetAInfo.BlockchainID, database.AllAllowedAddress, database.AllAllowedAddress) // Modify the JSON database to force the relayer to re-process old blocks - jsonDB.Put(relayerIDA, database.LatestProcessedBlockKey, []byte("0")) - jsonDB.Put(relayerIDB, database.LatestProcessedBlockKey, []byte("0")) + err = jsonDB.Put(relayerIDA, database.LatestProcessedBlockKey, []byte("0")) + Expect(err).Should(BeNil()) + err = jsonDB.Put(relayerIDB, database.LatestProcessedBlockKey, []byte("0")) + Expect(err).Should(BeNil()) // Subscribe to the destination chain newHeadsB := make(chan *types.Header, 10) diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 1e1b8df5..d6c445e1 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -80,4 +80,7 @@ var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { ginkgo.It("Batch Message", func() { BatchRelay(localNetworkInstance) }) + ginkgo.FIt("Relay Message API", func() { + RelayMessageAPI(localNetworkInstance) + }) }) diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go new file mode 100644 index 00000000..bb56b5b2 --- /dev/null +++ b/tests/relay_message_api.go @@ -0,0 +1,90 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tests + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/ava-labs/awm-relayer/relayer" + + testUtils "github.com/ava-labs/awm-relayer/tests/utils" + "github.com/ava-labs/teleporter/tests/interfaces" + "github.com/ava-labs/teleporter/tests/utils" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + . "github.com/onsi/gomega" +) + +func RelayMessageAPI(network interfaces.LocalNetwork) { + ctx := context.Background() + subnetAInfo := network.GetPrimaryNetworkInfo() + subnetBInfo, _ := utils.GetTwoSubnets(network) + fundedAddress, fundedKey := network.GetFundedAccountInfo() + teleporterContractAddress := network.GetTeleporterContractAddress() + err := testUtils.ClearRelayerStorage() + Expect(err).Should(BeNil()) + + log.Info("Funding relayer address on all subnets") + relayerKey, err := crypto.GenerateKey() + Expect(err).Should(BeNil()) + testUtils.FundRelayers(ctx, []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, fundedKey, relayerKey) + + log.Info("Sending teleporter messages") + receipt1, _, _ := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) + warpMessage1 := getWarpMessageFromLog(ctx, receipt1, subnetAInfo) + receipt2, _, _ := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) + warpMessage2 := getWarpMessageFromLog(ctx, receipt2, subnetAInfo) + + // Set up relayer config + relayerConfig := testUtils.CreateDefaultRelayerConfig( + []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, + []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, + teleporterContractAddress, + fundedAddress, + relayerKey, + ) + // Don't process missed blocks, so we can manually relay + relayerConfig.ProcessMissedBlocks = false + + relayerConfigPath := testUtils.WriteRelayerConfig(relayerConfig, testUtils.DefaultRelayerCfgFname) + + log.Info("Starting the relayer") + relayerCleanup := testUtils.BuildAndRunRelayerExecutable(ctx, relayerConfigPath) + defer relayerCleanup() + + // Sleep for some time to make sure relayer has started up and subscribed. + log.Info("Waiting for the relayer to start up") + time.Sleep(15 * time.Second) + + reqBody := relayer.RelayMessageRequest{ + BlockchainID: subnetAInfo.BlockchainID.String(), + MessageID: warpMessage1.ID().String(), + BlockNum: receipt1.BlockNumber.String(), + } + + b, err := json.Marshal(reqBody) + Expect(err).Should(BeNil()) + bodyReader := bytes.NewReader(b) + + requestURL := fmt.Sprintf("http://localhost:%d", relayerConfig.APIPort) + req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader) + Expect(err).Should(BeNil()) + + client := http.Client{ + Timeout: 30 * time.Second, + } + + res, err := client.Do(req) + Expect(err).Should(BeNil()) + + res. + + // Cancel the command and stop the relayer + relayerCleanup() +} From 770383126a5ca557328696264690f83ef135283a Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 11 Jun 2024 12:35:31 -0400 Subject: [PATCH 07/70] Add test for API --- relayer/message_coordinator.go | 2 +- tests/e2e_test.go | 2 +- tests/relay_message_api.go | 8 ++++---- types/types.go | 27 --------------------------- 4 files changed, 6 insertions(+), 33 deletions(-) diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index e27a42bd..c14127af 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -11,10 +11,10 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/awm-relayer/database" - "github.com/ava-labs/awm-relayer/ethclient" "github.com/ava-labs/awm-relayer/messages" relayerTypes "github.com/ava-labs/awm-relayer/types" "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/ethclient" "github.com/ethereum/go-ethereum/common" "go.uber.org/zap" ) diff --git a/tests/e2e_test.go b/tests/e2e_test.go index d6c445e1..7973893f 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -80,7 +80,7 @@ var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { ginkgo.It("Batch Message", func() { BatchRelay(localNetworkInstance) }) - ginkgo.FIt("Relay Message API", func() { + ginkgo.It("Relay Message API", func() { RelayMessageAPI(localNetworkInstance) }) }) diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index bb56b5b2..ae52b7db 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -40,6 +40,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { warpMessage1 := getWarpMessageFromLog(ctx, receipt1, subnetAInfo) receipt2, _, _ := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) warpMessage2 := getWarpMessageFromLog(ctx, receipt2, subnetAInfo) + warpMessage2.ID() // Set up relayer config relayerConfig := testUtils.CreateDefaultRelayerConfig( @@ -82,9 +83,8 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { res, err := client.Do(req) Expect(err).Should(BeNil()) + Expect(res.Status).Should(Equal(http.StatusOK)) - res. - - // Cancel the command and stop the relayer - relayerCleanup() + // Cancel the command and stop the relayer + relayerCleanup() } diff --git a/types/types.go b/types/types.go index f5e8b94b..8029b038 100644 --- a/types/types.go +++ b/types/types.go @@ -6,9 +6,7 @@ package types import ( "context" "errors" - "fmt" "math/big" - "time" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/awm-relayer/utils" @@ -126,28 +124,3 @@ func FetchWarpMessageFromID(ethClient ethclient.Client, warpMessageID common.Has return NewWarpMessageInfo(logs[0]) } - -// The node serving the filter logs request may be behind the node serving the block header request, -// so we retry a few times to ensure we get the logs -func fetchWarpLogsWithRetries(ethClient ethclient.Client, blockNum *big.Int, numRetries int, retryInterval time.Duration) ([]types.Log, error) { - var ( - logs []types.Log - err error - ) - - for i := 0; i < numRetries; i++ { - logs, err = ethClient.FilterLogs(context.Background(), interfaces.FilterQuery{ - Topics: [][]common.Hash{{WarpPrecompileLogFilter}, nil}, - Addresses: []common.Address{warp.ContractAddress}, - FromBlock: blockNum, - ToBlock: blockNum, - }) - if err == nil { - return logs, nil - } - if i != numRetries-1 { - time.Sleep(retryInterval) - } - } - return nil, fmt.Errorf("failed to fetch warp logs for block %d after %d retries: %w", blockNum.Uint64(), filterLogsRetries, err) -} From eb3e4a0be9eee3c89b897eb568b695eed7fe18d7 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 11 Jun 2024 12:39:40 -0400 Subject: [PATCH 08/70] lint --- main/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.go b/main/main.go index 1302033b..9930c60c 100644 --- a/main/main.go +++ b/main/main.go @@ -325,7 +325,7 @@ func createSourceClients( clients := make(map[ids.ID]ethclient.Client) for _, sourceBlockchain := range cfg.SourceBlockchains { - clients[sourceBlockchain.GetBlockchainID()], err = ethclient.DialWithConfig( + clients[sourceBlockchain.GetBlockchainID()], err = utils.DialWithConfig( ctx, sourceBlockchain.RPCEndpoint.BaseURL, sourceBlockchain.RPCEndpoint.HTTPHeaders, From d4042c678dfaaa77e8d713dd4595821ea19a1463 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 11 Jun 2024 16:44:05 -0400 Subject: [PATCH 09/70] Fix test --- main/main.go | 2 +- relayer/message_coordinator.go | 4 ++++ tests/relay_message_api.go | 16 +++++++--------- tests/utils/utils.go | 1 + types/types.go | 9 +++++---- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/main/main.go b/main/main.go index 9930c60c..3b2ae61a 100644 --- a/main/main.go +++ b/main/main.go @@ -214,7 +214,7 @@ func main() { logger.Fatal("Failed to create Application Relayers", zap.Error(err)) panic(err) } - relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers) + relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) // Initialize the API after the message coordinator is set http.Handle("/health", health.NewHandler(checker)) diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index c14127af..52db0367 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -33,11 +33,13 @@ func SetMessageCoordinator( logger logging.Logger, messageHandlerFactories map[ids.ID]map[common.Address]messages.MessageHandlerFactory, applicationRelayers map[common.Hash]*ApplicationRelayer, + sourceClients map[ids.ID]ethclient.Client, ) { globalMessageCoordinator = &MessageCoordinator{ logger: logger, MessageHandlerFactories: messageHandlerFactories, ApplicationRelayers: applicationRelayers, + SourceClients: sourceClients, } } @@ -211,11 +213,13 @@ func ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.In func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) error { ethClient, ok := mc.SourceClients[blockchainID] if !ok { + mc.logger.Error("Source client not found", zap.String("blockchainID", blockchainID.String())) return fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) } warpMessage, err := relayerTypes.FetchWarpMessageFromID(ethClient, messageID, blockNum) if err != nil { + mc.logger.Error("Failed to fetch warp from blockchain", zap.String("blockchainID", blockchainID.String()), zap.Error(err)) return err } diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index ae52b7db..c0a53593 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -36,11 +36,8 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { testUtils.FundRelayers(ctx, []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, fundedKey, relayerKey) log.Info("Sending teleporter messages") - receipt1, _, _ := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) - warpMessage1 := getWarpMessageFromLog(ctx, receipt1, subnetAInfo) - receipt2, _, _ := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) - warpMessage2 := getWarpMessageFromLog(ctx, receipt2, subnetAInfo) - warpMessage2.ID() + receipt, _, _ := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) + warpMessage := getWarpMessageFromLog(ctx, receipt, subnetAInfo) // Set up relayer config relayerConfig := testUtils.CreateDefaultRelayerConfig( @@ -65,17 +62,18 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { reqBody := relayer.RelayMessageRequest{ BlockchainID: subnetAInfo.BlockchainID.String(), - MessageID: warpMessage1.ID().String(), - BlockNum: receipt1.BlockNumber.String(), + MessageID: warpMessage.ID().Hex(), + BlockNum: receipt.BlockNumber.String(), } b, err := json.Marshal(reqBody) Expect(err).Should(BeNil()) bodyReader := bytes.NewReader(b) - requestURL := fmt.Sprintf("http://localhost:%d", relayerConfig.APIPort) + requestURL := fmt.Sprintf("http://localhost:%d/relay-message", relayerConfig.APIPort) req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader) Expect(err).Should(BeNil()) + req.Header.Set("Content-Type", "application/json") client := http.Client{ Timeout: 30 * time.Second, @@ -83,7 +81,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { res, err := client.Do(req) Expect(err).Should(BeNil()) - Expect(res.Status).Should(Equal(http.StatusOK)) + Expect(res.Status).Should(Equal("200 OK")) // Cancel the command and stop the relayer relayerCleanup() diff --git a/tests/utils/utils.go b/tests/utils/utils.go index 54b4bc6d..69aff8e7 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -203,6 +203,7 @@ func CreateDefaultRelayerConfig( ProcessMissedBlocks: false, SourceBlockchains: sources, DestinationBlockchains: destinations, + APIPort: 1234, } } diff --git a/types/types.go b/types/types.go index 8029b038..83f53777 100644 --- a/types/types.go +++ b/types/types.go @@ -6,6 +6,7 @@ package types import ( "context" "errors" + "fmt" "math/big" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" @@ -108,18 +109,18 @@ func UnpackWarpMessage(unsignedMsgBytes []byte) (*avalancheWarp.UnsignedMessage, return unsignedMsg, nil } -func FetchWarpMessageFromID(ethClient ethclient.Client, warpMessageID common.Hash, blockNum *big.Int) (*WarpMessageInfo, error) { +func FetchWarpMessageFromID(ethClient ethclient.Client, warpID common.Hash, blockNum *big.Int) (*WarpMessageInfo, error) { logs, err := ethClient.FilterLogs(context.Background(), interfaces.FilterQuery{ - Topics: [][]common.Hash{{WarpPrecompileLogFilter}, nil, {warpMessageID}}, + Topics: [][]common.Hash{{WarpPrecompileLogFilter}, nil, {warpID}}, Addresses: []common.Address{warp.ContractAddress}, FromBlock: blockNum, ToBlock: blockNum, }) if err != nil { - return nil, err + return nil, fmt.Errorf("could not fetch logs: %w", err) } if len(logs) != 1 { - return nil, ErrInvalidLog + return nil, fmt.Errorf("found more than 1 log: %d", len(logs)) } return NewWarpMessageInfo(logs[0]) From 9081636e6986cbd2e7ec1d7662bc94a2c526996b Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 11 Jun 2024 19:58:25 -0400 Subject: [PATCH 10/70] Fix test --- tests/basic_relay.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/basic_relay.go b/tests/basic_relay.go index 0021a8ba..2be9c9bb 100644 --- a/tests/basic_relay.go +++ b/tests/basic_relay.go @@ -114,10 +114,8 @@ func BasicRelay(network interfaces.LocalNetwork) { relayerIDA := database.CalculateRelayerID(subnetAInfo.BlockchainID, subnetBInfo.BlockchainID, database.AllAllowedAddress, database.AllAllowedAddress) relayerIDB := database.CalculateRelayerID(subnetBInfo.BlockchainID, subnetAInfo.BlockchainID, database.AllAllowedAddress, database.AllAllowedAddress) // Modify the JSON database to force the relayer to re-process old blocks - err = jsonDB.Put(relayerIDA, database.LatestProcessedBlockKey, []byte("0")) - Expect(err).Should(BeNil()) - err = jsonDB.Put(relayerIDB, database.LatestProcessedBlockKey, []byte("0")) - Expect(err).Should(BeNil()) + _ = jsonDB.Put(relayerIDA, database.LatestProcessedBlockKey, []byte("0")) + _ = jsonDB.Put(relayerIDB, database.LatestProcessedBlockKey, []byte("0")) // Subscribe to the destination chain newHeadsB := make(chan *types.Header, 10) From 818f650b290371b06d470786350b3e71e55fe8a3 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 12 Jun 2024 11:22:50 -0400 Subject: [PATCH 11/70] small fixes --- main/main.go | 2 +- relayer/application_relayer.go | 4 +-- relayer/relay_message_api_handler.go | 9 ++++-- tests/relay_message_api.go | 45 ++++++++++++++++++++-------- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/main/main.go b/main/main.go index 867c7ae9..b7c7e1a7 100644 --- a/main/main.go +++ b/main/main.go @@ -219,7 +219,7 @@ func main() { // Initialize the API after the message coordinator is set http.Handle("/health", health.NewHandler(checker)) - http.Handle("/relay-message", relayer.RelayMessageAPIHandler()) + http.Handle(relayer.RelayMessageApiPath, relayer.RelayMessageAPIHandler()) // start the health check server go func() { diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 31d0f78c..fa2ef4c5 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -167,13 +167,11 @@ func (r *ApplicationRelayer) ProcessMessage(handler messages.MessageHandler) err reqID := r.currentRequestID r.lock.Unlock() - err := r.relayMessage( + return r.relayMessage( reqID, handler, true, ) - - return err } func (r *ApplicationRelayer) RelayerID() database.RelayerID { diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go index 71cb9f2b..334f537c 100644 --- a/relayer/relay_message_api_handler.go +++ b/relayer/relay_message_api_handler.go @@ -9,10 +9,15 @@ import ( "github.com/ethereum/go-ethereum/common" ) +const RelayMessageApiPath = "/relay-message" + type RelayMessageRequest struct { + // cb58 encoding of the blockchain ID BlockchainID string `json:"blockchain-id"` - MessageID string `json:"message-id"` - BlockNum string `json:"block-num"` + // Hex encoding of the warp message ID + MessageID string `json:"message-id"` + // Integer representation of the block number + BlockNum string `json:"block-num"` } func RelayMessageAPIHandler() http.HandlerFunc { diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index c0a53593..d39cfc59 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -35,7 +35,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { Expect(err).Should(BeNil()) testUtils.FundRelayers(ctx, []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, fundedKey, relayerKey) - log.Info("Sending teleporter messages") + log.Info("Sending teleporter message") receipt, _, _ := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) warpMessage := getWarpMessageFromLog(ctx, receipt, subnetAInfo) @@ -66,22 +66,41 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { BlockNum: receipt.BlockNumber.String(), } - b, err := json.Marshal(reqBody) - Expect(err).Should(BeNil()) - bodyReader := bytes.NewReader(b) - - requestURL := fmt.Sprintf("http://localhost:%d/relay-message", relayerConfig.APIPort) - req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader) - Expect(err).Should(BeNil()) - req.Header.Set("Content-Type", "application/json") - client := http.Client{ Timeout: 30 * time.Second, } - res, err := client.Do(req) - Expect(err).Should(BeNil()) - Expect(res.Status).Should(Equal("200 OK")) + requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, relayer.RelayMessageApiPath) + + // Send request to API + { + b, err := json.Marshal(reqBody) + Expect(err).Should(BeNil()) + bodyReader := bytes.NewReader(b) + + req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader) + Expect(err).Should(BeNil()) + req.Header.Set("Content-Type", "application/json") + + res, err := client.Do(req) + Expect(err).Should(BeNil()) + Expect(res.Status).Should(Equal("200 OK")) + } + + // Send the same request to ensure the correct response. + { + b, err := json.Marshal(reqBody) + Expect(err).Should(BeNil()) + bodyReader := bytes.NewReader(b) + + req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader) + Expect(err).Should(BeNil()) + req.Header.Set("Content-Type", "application/json") + + res, err := client.Do(req) + Expect(err).Should(BeNil()) + Expect(res.Status).Should(Equal("200 OK")) + } // Cancel the command and stop the relayer relayerCleanup() From 83259434dd194d620b449337866f5d5435f7f0bc Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 14 Jun 2024 10:48:01 -0400 Subject: [PATCH 12/70] Add error messages --- relayer/message_coordinator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 52db0367..ca6d5511 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -220,7 +220,7 @@ func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID comm warpMessage, err := relayerTypes.FetchWarpMessageFromID(ethClient, messageID, blockNum) if err != nil { mc.logger.Error("Failed to fetch warp from blockchain", zap.String("blockchainID", blockchainID.String()), zap.Error(err)) - return err + return fmt.Errorf("could not fetch warp message from ID: %w", err) } appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) @@ -230,7 +230,7 @@ func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID comm zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), zap.Error(err), ) - return err + return fmt.Errorf("error getting application relayer: %w", err) } if appRelayer == nil { mc.logger.Error("Application relayer not found") From 23a9b2adaa7acee9439290223a24eb71b358a77a Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 14 Jun 2024 12:35:09 -0400 Subject: [PATCH 13/70] Update relayer/relay_message_api_handler.go Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> Signed-off-by: Geoff Stuart --- relayer/relay_message_api_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go index 334f537c..1b8f5d24 100644 --- a/relayer/relay_message_api_handler.go +++ b/relayer/relay_message_api_handler.go @@ -32,7 +32,7 @@ func RelayMessageAPIHandler() http.HandlerFunc { blockchainID, err := ids.FromString(req.BlockchainID) if err != nil { - http.Error(w, "invalid blockchainID"+err.Error(), http.StatusBadRequest) + http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) return } messageID := common.HexToHash(req.MessageID) From 6dc2441bb03dc49a9e1be1f5c768c913dda73254 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 18 Jun 2024 12:31:05 -0400 Subject: [PATCH 14/70] Review fixes --- main/main.go | 2 +- relayer/listener.go | 36 ++++++++-------- relayer/relay_message_api_handler.go | 64 ++++++++++++++-------------- 3 files changed, 49 insertions(+), 53 deletions(-) diff --git a/main/main.go b/main/main.go index b7c7e1a7..ce816564 100644 --- a/main/main.go +++ b/main/main.go @@ -219,7 +219,7 @@ func main() { // Initialize the API after the message coordinator is set http.Handle("/health", health.NewHandler(checker)) - http.Handle(relayer.RelayMessageApiPath, relayer.RelayMessageAPIHandler()) + http.HandleFunc(relayer.RelayMessageApiPath, relayer.RelayMessageAPIHandler) // start the health check server go func() { diff --git a/relayer/listener.go b/relayer/listener.go index d422efd2..da722248 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -28,15 +28,14 @@ const ( // Listener handles all messages sent from a given source chain type Listener struct { - Subscriber vms.Subscriber - currentRequestID uint32 - contractMessage vms.ContractMessage - logger logging.Logger - sourceBlockchain config.SourceBlockchain - catchUpResultChan chan bool - healthStatus *atomic.Bool - processMissedBlocks bool - ethClient ethclient.Client + Subscriber vms.Subscriber + currentRequestID uint32 + contractMessage vms.ContractMessage + logger logging.Logger + sourceBlockchain config.SourceBlockchain + catchUpResultChan chan bool + healthStatus *atomic.Bool + ethClient ethclient.Client } // runListener creates a Listener instance and the ApplicationRelayers for a subnet. @@ -123,15 +122,14 @@ func newListener( zap.String("blockchainIDHex", sourceBlockchain.GetBlockchainID().Hex()), ) lstnr := Listener{ - Subscriber: sub, - currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision - contractMessage: vms.NewContractMessage(logger, sourceBlockchain), - logger: logger, - sourceBlockchain: sourceBlockchain, - catchUpResultChan: catchUpResultChan, - healthStatus: relayerHealth, - processMissedBlocks: processMissedBlocks, - ethClient: ethRPCClient, + Subscriber: sub, + currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision + contractMessage: vms.NewContractMessage(logger, sourceBlockchain), + logger: logger, + sourceBlockchain: sourceBlockchain, + catchUpResultChan: catchUpResultChan, + healthStatus: relayerHealth, + ethClient: ethRPCClient, } // Open the subscription. We must do this before processing any missed messages, otherwise we may miss an incoming message @@ -145,7 +143,7 @@ func newListener( return nil, err } - if lstnr.processMissedBlocks { + if processMissedBlocks { // Process historical blocks in a separate goroutine so that the main processing loop can // start processing new blocks as soon as possible. Otherwise, it's possible for // ProcessFromHeight to overload the message queue and cause a deadlock. diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go index 1b8f5d24..5c1d5a43 100644 --- a/relayer/relay_message_api_handler.go +++ b/relayer/relay_message_api_handler.go @@ -9,45 +9,43 @@ import ( "github.com/ethereum/go-ethereum/common" ) -const RelayMessageApiPath = "/relay-message" +const RelayMessageApiPath = "/relay" type RelayMessageRequest struct { - // cb58 encoding of the blockchain ID + // Required. cb58 encoding of the blockchain ID BlockchainID string `json:"blockchain-id"` - // Hex encoding of the warp message ID + // Required. Hex encoding of the warp message ID MessageID string `json:"message-id"` - // Integer representation of the block number + // Required. Integer representation of the block number BlockNum string `json:"block-num"` } -func RelayMessageAPIHandler() http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req RelayMessageRequest - - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - blockchainID, err := ids.FromString(req.BlockchainID) - if err != nil { - http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) - return - } - messageID := common.HexToHash(req.MessageID) - blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) - if !ok { - http.Error(w, "invalid blockNum", http.StatusBadRequest) - return - } - - err = ProcessMessage(blockchainID, messageID, blockNum) - if err != nil { - http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) - return - } - - _, _ = w.Write([]byte("Message processed successfully")) +func RelayMessageAPIHandler(w http.ResponseWriter, r *http.Request) { + var req RelayMessageRequest + + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + blockchainID, err := ids.FromString(req.BlockchainID) + if err != nil { + http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) + return + } + messageID := common.HexToHash(req.MessageID) + blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) + if !ok { + http.Error(w, "invalid blockNum", http.StatusBadRequest) + return } + + err = ProcessMessage(blockchainID, messageID, blockNum) + if err != nil { + http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write([]byte("Message processed successfully")) } From 177c15fc5b7875e6221bca00315f444a179cc42e Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 18 Jun 2024 15:25:45 -0400 Subject: [PATCH 15/70] Update main/main.go Co-authored-by: minghinmatthewlam Signed-off-by: Geoff Stuart --- main/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.go b/main/main.go index ce816564..bfeb0917 100644 --- a/main/main.go +++ b/main/main.go @@ -111,7 +111,7 @@ func main() { } // Initialize all source clients - logger.Info("Initializing destination clients") + logger.Info("Initializing source clients") sourceClients, err := createSourceClients(context.Background(), logger, &cfg) if err != nil { logger.Fatal("Failed to create source clients", zap.Error(err)) From cf3c779dece76c20f31431ed3bdf9e20d7527c48 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 18 Jun 2024 15:26:26 -0400 Subject: [PATCH 16/70] Update main/main.go Co-authored-by: minghinmatthewlam Signed-off-by: Geoff Stuart --- main/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.go b/main/main.go index bfeb0917..66c226f7 100644 --- a/main/main.go +++ b/main/main.go @@ -195,7 +195,7 @@ func main() { messageHandlerFactories, err := createMessageHandlerFactories(logger, &cfg) if err != nil { - logger.Fatal("Failed to create Message Handler Factories", zap.Error(err)) + logger.Fatal("Failed to create message handler factories", zap.Error(err)) panic(err) } From 664cd310d106ce35f8615d903f80529a840e753b Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 18 Jun 2024 15:27:54 -0400 Subject: [PATCH 17/70] Update main/main.go Co-authored-by: minghinmatthewlam Signed-off-by: Geoff Stuart --- main/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/main.go b/main/main.go index 66c226f7..920a8969 100644 --- a/main/main.go +++ b/main/main.go @@ -212,7 +212,8 @@ func main() { destinationClients, ) if err != nil { - logger.Fatal("Failed to create Application Relayers", zap.Error(err)) + logger.Fatal("Failed to create application relayers", zap.Error(err)) +`` panic(err) } relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) From 0e650395c7f10ed137cccb038d906ab297f2772e Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 18 Jun 2024 15:28:03 -0400 Subject: [PATCH 18/70] Update main/main.go Co-authored-by: minghinmatthewlam Signed-off-by: Geoff Stuart --- main/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.go b/main/main.go index 920a8969..e164bb96 100644 --- a/main/main.go +++ b/main/main.go @@ -308,7 +308,7 @@ func createMessageHandlerFactories( m, err = nil, fmt.Errorf("invalid message format %s", format) } if err != nil { - logger.Error("Failed to create message manager", zap.Error(err)) + logger.Error("Failed to create message handler factory", zap.Error(err)) return nil, err } messageHandlerFactoriesForSource[address] = m From fec26c654112348e998e70fbc5402d4763cc3a1f Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 18 Jun 2024 15:29:44 -0400 Subject: [PATCH 19/70] Fix typo --- main/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/main/main.go b/main/main.go index e164bb96..f8df4f80 100644 --- a/main/main.go +++ b/main/main.go @@ -213,7 +213,6 @@ func main() { ) if err != nil { logger.Fatal("Failed to create application relayers", zap.Error(err)) -`` panic(err) } relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) From 56f4c384878f3945d3654102b93caee93f1ef74e Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 19 Jun 2024 13:19:35 -0400 Subject: [PATCH 20/70] Add txhash to SendMessage --- messages/message_handler.go | 2 +- .../off-chain-registry/message_handler.go | 10 ++++----- messages/teleporter/message_handler.go | 16 +++++++------- relayer/application_relayer.go | 22 ++++++++++--------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/messages/message_handler.go b/messages/message_handler.go index 1ebfc04c..428a8fd1 100644 --- a/messages/message_handler.go +++ b/messages/message_handler.go @@ -27,7 +27,7 @@ type MessageHandler interface { // SendMessage sends the signed message to the destination chain. The payload parsed according to // the VM rules is also passed in, since MessageManager does not assume any particular VM - SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) error + SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) (common.Hash, error) // GetMessageRoutingInfo returns the source chain ID, origin sender address, destination chain ID, and destination address GetMessageRoutingInfo() ( diff --git a/messages/off-chain-registry/message_handler.go b/messages/off-chain-registry/message_handler.go index 8abd17e4..bf6ca351 100644 --- a/messages/off-chain-registry/message_handler.go +++ b/messages/off-chain-registry/message_handler.go @@ -144,7 +144,7 @@ func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClie return false, nil } -func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) error { +func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) (common.Hash, error) { // Construct the transaction call data to call the TeleporterRegistry contract. // Only one off-chain registry Warp message is sent at a time, so we hardcode the index to 0 in the call. callData, err := teleporterregistry.PackAddProtocolVersion(0) @@ -154,10 +154,10 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli zap.String("destinationBlockchainID", destinationClient.DestinationBlockchainID().String()), zap.String("warpMessageID", signedMessage.ID().String()), ) - return err + return common.Hash{}, err } - _, err = destinationClient.SendTx(signedMessage, m.factory.registryAddress.Hex(), addProtocolVersionGasLimit, callData) + txHash, err := destinationClient.SendTx(signedMessage, m.factory.registryAddress.Hex(), addProtocolVersionGasLimit, callData) if err != nil { m.logger.Error( "Failed to send tx.", @@ -165,14 +165,14 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli zap.String("warpMessageID", signedMessage.ID().String()), zap.Error(err), ) - return err + return common.Hash{}, err } m.logger.Info( "Sent message to destination chain", zap.String("destinationBlockchainID", destinationClient.DestinationBlockchainID().String()), zap.String("warpMessageID", signedMessage.ID().String()), ) - return nil + return txHash, nil } func (m *messageHandler) GetMessageRoutingInfo() ( diff --git a/messages/teleporter/message_handler.go b/messages/teleporter/message_handler.go index 806d254a..0e58e044 100644 --- a/messages/teleporter/message_handler.go +++ b/messages/teleporter/message_handler.go @@ -172,7 +172,7 @@ func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClie // SendMessage extracts the gasLimit and packs the call data to call the receiveCrossChainMessage method of the Teleporter contract, // and dispatches transaction construction and broadcast to the destination client -func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) error { +func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) (common.Hash, error) { destinationBlockchainID := destinationClient.DestinationBlockchainID() teleporterMessageID, err := teleporterUtils.CalculateMessageID( m.factory.protocolAddress, @@ -181,7 +181,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli m.teleporterMessage.MessageNonce, ) if err != nil { - return fmt.Errorf("failed to calculate Teleporter message ID: %w", err) + return common.Hash{}, fmt.Errorf("failed to calculate Teleporter message ID: %w", err) } m.logger.Info( @@ -198,7 +198,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli zap.String("warpMessageID", signedMessage.ID().String()), zap.String("teleporterMessageID", teleporterMessageID.String()), ) - return err + return common.Hash{}, err } gasLimit, err := gasUtils.CalculateReceiveMessageGasLimit( @@ -215,7 +215,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli zap.String("warpMessageID", signedMessage.ID().String()), zap.String("teleporterMessageID", teleporterMessageID.String()), ) - return err + return common.Hash{}, err } // Construct the transaction call data to call the receive cross chain message method of the receiver precompile. callData, err := teleportermessenger.PackReceiveCrossChainMessage(0, common.HexToAddress(m.factory.messageConfig.RewardAddress)) @@ -226,7 +226,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli zap.String("warpMessageID", signedMessage.ID().String()), zap.String("teleporterMessageID", teleporterMessageID.String()), ) - return err + return common.Hash{}, err } txHash, err := destinationClient.SendTx(signedMessage, m.factory.protocolAddress.Hex(), gasLimit, callData) @@ -238,13 +238,13 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli zap.String("teleporterMessageID", teleporterMessageID.String()), zap.Error(err), ) - return err + return common.Hash{}, err } // Wait for the message to be included in a block before returning err = m.waitForReceipt(signedMessage, destinationClient, txHash, teleporterMessageID) if err != nil { - return err + return common.Hash{}, err } m.logger.Info( @@ -254,7 +254,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli zap.String("teleporterMessageID", teleporterMessageID.String()), zap.String("txHash", txHash.String()), ) - return nil + return txHash, nil } func (m *messageHandler) waitForReceipt(signedMessage *warp.Message, destinationClient vms.DestinationClient, txHash common.Hash, teleporterMessageID ids.ID) error { diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 356f3e54..1755fd8d 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -31,6 +31,7 @@ import ( coreEthMsg "github.com/ava-labs/coreth/plugin/evm/message" msg "github.com/ava-labs/subnet-evm/plugin/evm/message" warpBackend "github.com/ava-labs/subnet-evm/warp" + "github.com/ethereum/go-ethereum/common" "golang.org/x/sync/errgroup" "go.uber.org/zap" @@ -136,7 +137,8 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, handlers []messages.Me // Once we upgrade to Go 1.22, we can use the loop variable directly in the goroutine h := handler eg.Go(func() error { - return r.ProcessMessage(h) + _, err := r.ProcessMessage(h) + return err }) } if err := eg.Wait(); err != nil { @@ -160,7 +162,7 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, handlers []messages.Me } // Relays a message to the destination chain. Does not checkpoint the height. -func (r *ApplicationRelayer) ProcessMessage(handler messages.MessageHandler) error { +func (r *ApplicationRelayer) ProcessMessage(handler messages.MessageHandler) (common.Hash, error) { // Increment the request ID. Make sure we don't hold the lock while we relay the message. r.lock.Lock() r.currentRequestID++ @@ -182,7 +184,7 @@ func (r *ApplicationRelayer) relayMessage( requestID uint32, handler messages.MessageHandler, useAppRequestNetwork bool, -) error { +) (common.Hash, error) { r.logger.Debug( "Relaying message", zap.Uint32("requestID", requestID), @@ -196,11 +198,11 @@ func (r *ApplicationRelayer) relayMessage( zap.Error(err), ) r.incFailedRelayMessageCount("failed to check if message should be sent") - return err + return common.Hash{}, err } if !shouldSend { r.logger.Info("Message should not be sent") - return nil + return common.Hash{}, nil } unsignedMessage := handler.GetUnsignedMessage() @@ -215,7 +217,7 @@ func (r *ApplicationRelayer) relayMessage( zap.Error(err), ) r.incFailedRelayMessageCount("failed to create signed warp message via AppRequest network") - return err + return common.Hash{}, err } } else { signedMessage, err = r.createSignedMessage(unsignedMessage) @@ -225,21 +227,21 @@ func (r *ApplicationRelayer) relayMessage( zap.Error(err), ) r.incFailedRelayMessageCount("failed to create signed warp message via RPC") - return err + return common.Hash{}, err } } // create signed message latency (ms) r.setCreateSignedMessageLatencyMS(float64(time.Since(startCreateSignedMessageTime).Milliseconds())) - err = handler.SendMessage(signedMessage, r.destinationClient) + txHash, err := handler.SendMessage(signedMessage, r.destinationClient) if err != nil { r.logger.Error( "Failed to send warp message", zap.Error(err), ) r.incFailedRelayMessageCount("failed to send warp message") - return err + return common.Hash{}, err } r.logger.Info( "Finished relaying message to destination chain", @@ -247,7 +249,7 @@ func (r *ApplicationRelayer) relayMessage( ) r.incSuccessfulRelayMessageCount() - return nil + return txHash, nil } // createSignedMessage fetches the signed Warp message from the source chain via RPC. From 78632811a6595f7e2e99ddfda90b5ae1f39c1eb2 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 20 Jun 2024 11:40:52 -0400 Subject: [PATCH 21/70] Fix --- relayer/message_coordinator.go | 14 +++++++------- relayer/relay_message_api_handler.go | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index ca6d5511..9d1e4d84 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -190,7 +190,7 @@ func (mc *MessageCoordinator) processManualWarpMessages( ) return err } - err = appRelayer.ProcessMessage(handler) + _, err = appRelayer.ProcessMessage(handler) if err != nil { mc.logger.Error( "Failed to process manual Warp message", @@ -203,24 +203,24 @@ func (mc *MessageCoordinator) processManualWarpMessages( return nil } -func ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) error { +func ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { if globalMessageCoordinator == nil { panic("global message coordinator not set") } return globalMessageCoordinator.processMessage(blockchainID, messageID, blockNum) } -func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) error { +func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { ethClient, ok := mc.SourceClients[blockchainID] if !ok { mc.logger.Error("Source client not found", zap.String("blockchainID", blockchainID.String())) - return fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) + return common.Hash{}, fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) } warpMessage, err := relayerTypes.FetchWarpMessageFromID(ethClient, messageID, blockNum) if err != nil { mc.logger.Error("Failed to fetch warp from blockchain", zap.String("blockchainID", blockchainID.String()), zap.Error(err)) - return fmt.Errorf("could not fetch warp message from ID: %w", err) + return common.Hash{}, fmt.Errorf("could not fetch warp message from ID: %w", err) } appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) @@ -230,11 +230,11 @@ func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID comm zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), zap.Error(err), ) - return fmt.Errorf("error getting application relayer: %w", err) + return common.Hash{}, fmt.Errorf("error getting application relayer: %w", err) } if appRelayer == nil { mc.logger.Error("Application relayer not found") - return errors.New("application relayer not found") + return common.Hash{}, errors.New("application relayer not found") } return appRelayer.ProcessMessage(handler) diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go index 5c1d5a43..eeada70d 100644 --- a/relayer/relay_message_api_handler.go +++ b/relayer/relay_message_api_handler.go @@ -41,11 +41,11 @@ func RelayMessageAPIHandler(w http.ResponseWriter, r *http.Request) { return } - err = ProcessMessage(blockchainID, messageID, blockNum) + txHash, err := ProcessMessage(blockchainID, messageID, blockNum) if err != nil { http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) return } - _, _ = w.Write([]byte("Message processed successfully")) + _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) } From 039e5d72f9953cf8f6519a6da1f686ca0b552863 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 20 Jun 2024 12:29:20 -0400 Subject: [PATCH 22/70] Add endpoint for manual warp messages --- config/config.go | 10 +-- config/manual_warp_message.go | 76 ---------------- main/main.go | 27 ------ relayer/manual_warp_message.go | 2 + relayer/message_coordinator.go | 48 ++++------ relayer/relay_message_api_handler.go | 46 +++++++++- tests/e2e_test.go | 3 - tests/manual_message.go | 129 --------------------------- tests/relay_message_api.go | 24 +++++ tests/teleporter_registry.go | 59 +++++++----- 10 files changed, 129 insertions(+), 295 deletions(-) delete mode 100644 config/manual_warp_message.go create mode 100644 relayer/manual_warp_message.go delete mode 100644 tests/manual_message.go diff --git a/config/config.go b/config/config.go index 4ae1fe00..fc4bc4c5 100644 --- a/config/config.go +++ b/config/config.go @@ -58,7 +58,6 @@ type Config struct { 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"` - ManualWarpMessages []*ManualWarpMessage `mapstructure:"manual-warp-messages" json:"manual-warp-messages"` // convenience field to fetch a blockchain's subnet ID blockchainIDToSubnetID map[ids.ID]ids.ID @@ -119,14 +118,7 @@ func (c *Config) Validate() error { blockchainIDToSubnetID[s.blockchainID] = s.subnetID } c.blockchainIDToSubnetID = blockchainIDToSubnetID - - // Validate the manual warp messages - for i, msg := range c.ManualWarpMessages { - if err := msg.Validate(); err != nil { - return fmt.Errorf("invalid manual warp message at index %d: %w", i, err) - } - } - + return nil } diff --git a/config/manual_warp_message.go b/config/manual_warp_message.go deleted file mode 100644 index db5b26b0..00000000 --- a/config/manual_warp_message.go +++ /dev/null @@ -1,76 +0,0 @@ -package config - -import ( - "encoding/hex" - "errors" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/awm-relayer/utils" - "github.com/ethereum/go-ethereum/common" -) - -// Defines a manual warp message to be sent from the relayer on startup. -type ManualWarpMessage struct { - UnsignedMessageBytes string `mapstructure:"unsigned-message-bytes" json:"unsigned-message-bytes"` - SourceBlockchainID string `mapstructure:"source-blockchain-id" json:"source-blockchain-id"` - DestinationBlockchainID string `mapstructure:"destination-blockchain-id" json:"destination-blockchain-id"` - SourceAddress string `mapstructure:"source-address" json:"source-address"` - DestinationAddress string `mapstructure:"destination-address" json:"destination-address"` - - // convenience fields to access the values after initialization - unsignedMessageBytes []byte - sourceBlockchainID ids.ID - destinationBlockchainID ids.ID - sourceAddress common.Address - destinationAddress common.Address -} - -// Validates the manual Warp message configuration. -// Does not modify the public fields as derived from the configuration passed to the application, -// but does initialize private fields available through getters -func (m *ManualWarpMessage) Validate() error { - unsignedMsg, err := hex.DecodeString(utils.SanitizeHexString(m.UnsignedMessageBytes)) - if err != nil { - return err - } - sourceBlockchainID, err := ids.FromString(m.SourceBlockchainID) - if err != nil { - return err - } - if !common.IsHexAddress(m.SourceAddress) { - return errors.New("invalid source address in manual warp message configuration") - } - destinationBlockchainID, err := ids.FromString(m.DestinationBlockchainID) - if err != nil { - return err - } - if !common.IsHexAddress(m.DestinationAddress) { - return errors.New("invalid destination address in manual warp message configuration") - } - m.unsignedMessageBytes = unsignedMsg - m.sourceBlockchainID = sourceBlockchainID - m.sourceAddress = common.HexToAddress(m.SourceAddress) - m.destinationBlockchainID = destinationBlockchainID - m.destinationAddress = common.HexToAddress(m.DestinationAddress) - return nil -} - -func (m *ManualWarpMessage) GetUnsignedMessageBytes() []byte { - return m.unsignedMessageBytes -} - -func (m *ManualWarpMessage) GetSourceBlockchainID() ids.ID { - return m.sourceBlockchainID -} - -func (m *ManualWarpMessage) GetSourceAddress() common.Address { - return m.sourceAddress -} - -func (m *ManualWarpMessage) GetDestinationBlockchainID() ids.ID { - return m.destinationBlockchainID -} - -func (m *ManualWarpMessage) GetDestinationAddress() common.Address { - return m.destinationAddress -} diff --git a/main/main.go b/main/main.go index f8df4f80..09c98658 100644 --- a/main/main.go +++ b/main/main.go @@ -5,7 +5,6 @@ package main import ( "context" - "encoding/hex" "fmt" "log" "net/http" @@ -26,8 +25,6 @@ import ( "github.com/ava-labs/awm-relayer/database" "github.com/ava-labs/awm-relayer/peers" "github.com/ava-labs/awm-relayer/relayer" - "github.com/ava-labs/awm-relayer/types" - relayerTypes "github.com/ava-labs/awm-relayer/types" "github.com/ava-labs/awm-relayer/utils" "github.com/ava-labs/awm-relayer/vms" "github.com/ava-labs/subnet-evm/ethclient" @@ -226,26 +223,6 @@ func main() { log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%d", cfg.APIPort), nil)) }() - // Gather manual Warp messages specified in the configuration - manualWarpMessages := make(map[ids.ID][]*relayerTypes.WarpMessageInfo) - for _, msg := range cfg.ManualWarpMessages { - sourceBlockchainID := msg.GetSourceBlockchainID() - unsignedMsg, err := types.UnpackWarpMessage(msg.GetUnsignedMessageBytes()) - if err != nil { - logger.Fatal( - "Failed to unpack manual Warp message", - zap.String("warpMessageBytes", hex.EncodeToString(msg.GetUnsignedMessageBytes())), - zap.Error(err), - ) - panic(err) - } - warpLogInfo := relayerTypes.WarpMessageInfo{ - SourceAddress: msg.GetSourceAddress(), - UnsignedMessage: unsignedMsg, - } - manualWarpMessages[sourceBlockchainID] = append(manualWarpMessages[sourceBlockchainID], &warpLogInfo) - } - // Create listeners for each of the subnets configured as a source errGroup, ctx := errgroup.WithContext(context.Background()) for _, s := range cfg.SourceBlockchains { @@ -254,10 +231,6 @@ func main() { isHealthy := atomic.NewBool(true) relayerHealth[s.GetBlockchainID()] = isHealthy - errGroup.Go(func() error { - return relayer.ProcessManualWarpMessages(manualWarpMessages[sourceBlockchain.GetBlockchainID()]) - }) - // errgroup will cancel the context when the first goroutine returns an error errGroup.Go(func() error { // runListener runs until it errors or the context is cancelled by another goroutine diff --git a/relayer/manual_warp_message.go b/relayer/manual_warp_message.go new file mode 100644 index 00000000..7848e514 --- /dev/null +++ b/relayer/manual_warp_message.go @@ -0,0 +1,2 @@ +package relayer + diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 9d1e4d84..d9d71eb6 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -164,43 +164,33 @@ func (mc *MessageCoordinator) getApplicationRelayer( return nil } -func ProcessManualWarpMessages(manualWarpMessages []*relayerTypes.WarpMessageInfo) error { +func ProcessManualWarpMessage(warpMessage *relayerTypes.WarpMessageInfo) (common.Hash, error) { if globalMessageCoordinator == nil { - return fmt.Errorf("global message coordinator not set") + return common.Hash{}, fmt.Errorf("global message coordinator not set") } - return globalMessageCoordinator.processManualWarpMessages(manualWarpMessages) + return globalMessageCoordinator.processManualWarpMessage(warpMessage) } -func (mc *MessageCoordinator) processManualWarpMessages( - manualWarpMessages []*relayerTypes.WarpMessageInfo, -) error { +func (mc *MessageCoordinator) processManualWarpMessage( + warpMessage *relayerTypes.WarpMessageInfo, +) (common.Hash, error) { // Send any messages that were specified in the configuration - for _, warpMessage := range manualWarpMessages { - mc.logger.Info( - "Relaying manual Warp message", - zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), + mc.logger.Info( + "Relaying manual Warp message", + zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), + zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), + ) + appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) + if err != nil { + mc.logger.Error( + "Failed to parse manual Warp message.", + zap.Error(err), zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), ) - appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) - if err != nil { - mc.logger.Error( - "Failed to parse manual Warp message.", - zap.Error(err), - zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), - ) - return err - } - _, err = appRelayer.ProcessMessage(handler) - if err != nil { - mc.logger.Error( - "Failed to process manual Warp message", - zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), - zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), - ) - return err - } + return common.Hash{}, err } - return nil + + return appRelayer.ProcessMessage(handler) } func ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go index eeada70d..5acb6fda 100644 --- a/relayer/relay_message_api_handler.go +++ b/relayer/relay_message_api_handler.go @@ -5,11 +5,17 @@ import ( "math/big" "net/http" + "github.com/ava-labs/awm-relayer/types" + relayerTypes "github.com/ava-labs/awm-relayer/types" + "github.com/ava-labs/avalanchego/ids" "github.com/ethereum/go-ethereum/common" ) -const RelayMessageApiPath = "/relay" +const ( + RelayApiPath = "/relay" + RelayMessageApiPath = RelayApiPath + "/message" +) type RelayMessageRequest struct { // Required. cb58 encoding of the blockchain ID @@ -20,6 +26,44 @@ type RelayMessageRequest struct { BlockNum string `json:"block-num"` } +// Defines a manual warp message to be sent from the relayer through the API. +type ManualWarpMessage struct { + UnsignedMessageBytes []byte + SourceBlockchainID ids.ID + DestinationBlockchainID ids.ID + SourceAddress common.Address + DestinationAddress common.Address +} + +func RelayAPIHandler(w http.ResponseWriter, r *http.Request) { + var req ManualWarpMessage + + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + unsignedMessage, err := types.UnpackWarpMessage(req.UnsignedMessageBytes) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + warpMessageInfo := &relayerTypes.WarpMessageInfo{ + SourceAddress: req.SourceAddress, + UnsignedMessage: unsignedMessage, + } + + txHash, err := ProcessManualWarpMessage(warpMessageInfo) + if err != nil { + http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) +} + func RelayMessageAPIHandler(w http.ResponseWriter, r *http.Request) { var req RelayMessageRequest diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 7973893f..b9af3b8f 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -68,9 +68,6 @@ var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { ginkgo.It("Basic Relay", func() { BasicRelay(localNetworkInstance) }) - ginkgo.It("Teleporter Registry", func() { - TeleporterRegistry(localNetworkInstance) - }) ginkgo.It("Shared Database", func() { SharedDatabaseAccess(localNetworkInstance) }) diff --git a/tests/manual_message.go b/tests/manual_message.go deleted file mode 100644 index 9a487d98..00000000 --- a/tests/manual_message.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package tests - -import ( - "context" - "encoding/hex" - "time" - - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/awm-relayer/config" - testUtils "github.com/ava-labs/awm-relayer/tests/utils" - "github.com/ava-labs/subnet-evm/accounts/abi/bind" - "github.com/ava-labs/subnet-evm/core/types" - subnetEvmInterfaces "github.com/ava-labs/subnet-evm/interfaces" - "github.com/ava-labs/subnet-evm/precompile/contracts/warp" - "github.com/ava-labs/teleporter/tests/interfaces" - "github.com/ava-labs/teleporter/tests/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - - . "github.com/onsi/gomega" -) - -// This tests relaying a message manually provided in the relayer config -func ManualMessage(network interfaces.LocalNetwork) { - subnetAInfo := network.GetPrimaryNetworkInfo() - subnetBInfo, _ := utils.GetTwoSubnets(network) - fundedAddress, fundedKey := network.GetFundedAccountInfo() - teleporterContractAddress := network.GetTeleporterContractAddress() - err := testUtils.ClearRelayerStorage() - Expect(err).Should(BeNil()) - - // - // Fund the relayer address on all subnets - // - ctx := context.Background() - - log.Info("Funding relayer address on all subnets") - relayerKey, err := crypto.GenerateKey() - Expect(err).Should(BeNil()) - testUtils.FundRelayers(ctx, []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, fundedKey, relayerKey) - - // - // Send two Teleporter message on Subnet A, before the relayer is running - // - - log.Info("Sending two teleporter messages on subnet A") - // This message will be delivered by the relayer - receipt1, _, id1 := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) - msg1 := getWarpMessageFromLog(ctx, receipt1, subnetAInfo) - - // This message will not be delivered by the relayer - _, _, id2 := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) - - // - // Set up relayer config to deliver one of the two previously sent messages - // - relayerConfig := testUtils.CreateDefaultRelayerConfig( - []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, - []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, - teleporterContractAddress, - fundedAddress, - relayerKey, - ) - relayerConfig.ManualWarpMessages = []*config.ManualWarpMessage{ - { - UnsignedMessageBytes: hex.EncodeToString(msg1.Bytes()), - SourceBlockchainID: subnetAInfo.BlockchainID.String(), - DestinationBlockchainID: subnetBInfo.BlockchainID.String(), - SourceAddress: teleporterContractAddress.Hex(), - DestinationAddress: teleporterContractAddress.Hex(), - }, - } - - relayerConfigPath := testUtils.WriteRelayerConfig(relayerConfig, testUtils.DefaultRelayerCfgFname) - - // - // Run the Relayer. On startup, we should deliver the message provided in the config - // - - // Subscribe to the destination chain - newHeadsB := make(chan *types.Header, 10) - sub, err := subnetBInfo.WSClient.SubscribeNewHead(ctx, newHeadsB) - Expect(err).Should(BeNil()) - defer sub.Unsubscribe() - - log.Info("Starting the relayer") - relayerCleanup := testUtils.BuildAndRunRelayerExecutable(ctx, relayerConfigPath) - defer relayerCleanup() - - log.Info("Waiting for a new block confirmation on subnet B") - <-newHeadsB - delivered1, err := subnetBInfo.TeleporterMessenger.MessageReceived( - &bind.CallOpts{}, id1, - ) - Expect(err).Should(BeNil()) - Expect(delivered1).Should(BeTrue()) - - log.Info("Waiting for 10s to ensure no new block confirmations on destination chain") - Consistently(newHeadsB, 10*time.Second, 500*time.Millisecond).ShouldNot(Receive()) - - delivered2, err := subnetBInfo.TeleporterMessenger.MessageReceived( - &bind.CallOpts{}, id2, - ) - Expect(err).Should(BeNil()) - Expect(delivered2).Should(BeFalse()) -} - -func getWarpMessageFromLog(ctx context.Context, receipt *types.Receipt, source interfaces.SubnetTestInfo) *avalancheWarp.UnsignedMessage { - log.Info("Fetching relevant warp logs from the newly produced block") - logs, err := source.RPCClient.FilterLogs(ctx, subnetEvmInterfaces.FilterQuery{ - BlockHash: &receipt.BlockHash, - Addresses: []common.Address{warp.Module.Address}, - }) - Expect(err).Should(BeNil()) - Expect(len(logs)).Should(Equal(1)) - - // Check for relevant warp log from subscription and ensure that it matches - // the log extracted from the last block. - txLog := logs[0] - log.Info("Parsing logData as unsigned warp message") - unsignedMsg, err := warp.UnpackSendWarpEventDataToMessage(txLog.Data) - Expect(err).Should(BeNil()) - - return unsignedMsg -} diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index d39cfc59..b7d34327 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -14,10 +14,15 @@ import ( "github.com/ava-labs/awm-relayer/relayer" testUtils "github.com/ava-labs/awm-relayer/tests/utils" + "github.com/ava-labs/subnet-evm/core/types" + subnetEvmInterfaces "github.com/ava-labs/subnet-evm/interfaces" + "github.com/ava-labs/subnet-evm/precompile/contracts/warp" "github.com/ava-labs/teleporter/tests/interfaces" "github.com/ava-labs/teleporter/tests/utils" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" . "github.com/onsi/gomega" ) @@ -105,3 +110,22 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { // Cancel the command and stop the relayer relayerCleanup() } + +func getWarpMessageFromLog(ctx context.Context, receipt *types.Receipt, source interfaces.SubnetTestInfo) *avalancheWarp.UnsignedMessage { + log.Info("Fetching relevant warp logs from the newly produced block") + logs, err := source.RPCClient.FilterLogs(ctx, subnetEvmInterfaces.FilterQuery{ + BlockHash: &receipt.BlockHash, + Addresses: []common.Address{warp.Module.Address}, + }) + Expect(err).Should(BeNil()) + Expect(len(logs)).Should(Equal(1)) + + // Check for relevant warp log from subscription and ensure that it matches + // the log extracted from the last block. + txLog := logs[0] + log.Info("Parsing logData as unsigned warp message") + unsignedMsg, err := warp.UnpackSendWarpEventDataToMessage(txLog.Data) + Expect(err).Should(BeNil()) + + return unsignedMsg +} diff --git a/tests/teleporter_registry.go b/tests/teleporter_registry.go index 13c4698e..de61079b 100644 --- a/tests/teleporter_registry.go +++ b/tests/teleporter_registry.go @@ -4,13 +4,17 @@ package tests import ( + "bytes" "context" - "encoding/hex" + "encoding/json" + "fmt" "math/big" + "net/http" + "time" runner_sdk "github.com/ava-labs/avalanche-network-runner/client" - "github.com/ava-labs/awm-relayer/config" offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" + "github.com/ava-labs/awm-relayer/relayer" testUtils "github.com/ava-labs/awm-relayer/tests/utils" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/core/types" @@ -25,7 +29,7 @@ import ( // Tests relayer support for off-chain Teleporter Registry updates // - Configures the relayer to send an off-chain message to the Teleporter Registry // - Verifies that the Teleporter Registry is updated -func TeleporterRegistry(network interfaces.LocalNetwork) { +func ManualMessage(network interfaces.LocalNetwork) { cChainInfo := network.GetPrimaryNetworkInfo() subnetAInfo, subnetBInfo := teleporterTestUtils.GetTwoSubnets(network) fundedAddress, fundedKey := network.GetFundedAccountInfo() @@ -88,19 +92,7 @@ func TeleporterRegistry(network interfaces.LocalNetwork) { fundedAddress, relayerKey, ) - relayerConfig.ManualWarpMessages = []*config.ManualWarpMessage{ - { - UnsignedMessageBytes: hex.EncodeToString(unsignedMessage.Bytes()), - SourceBlockchainID: cChainInfo.BlockchainID.String(), - DestinationBlockchainID: cChainInfo.BlockchainID.String(), - SourceAddress: offchainregistry.OffChainRegistrySourceAddress.Hex(), - DestinationAddress: cChainInfo.TeleporterRegistryAddress.Hex(), - }, - } relayerConfigPath := testUtils.WriteRelayerConfig(relayerConfig, testUtils.DefaultRelayerCfgFname) - // - // Run the Relayer. On startup, we should deliver the message provided in the config - // // Subscribe to the destination chain newHeadsC := make(chan *types.Header, 10) @@ -112,11 +104,36 @@ func TeleporterRegistry(network interfaces.LocalNetwork) { relayerCleanup := testUtils.BuildAndRunRelayerExecutable(ctx, relayerConfigPath) defer relayerCleanup() - log.Info("Waiting for a new block confirmation on the C-Chain") - <-newHeadsC + reqBody := relayer.ManualWarpMessage{ + UnsignedMessageBytes: unsignedMessage.Bytes(), + SourceBlockchainID: cChainInfo.BlockchainID, + DestinationBlockchainID: cChainInfo.BlockchainID, + SourceAddress: offchainregistry.OffChainRegistrySourceAddress, + DestinationAddress: cChainInfo.TeleporterRegistryAddress, + } - log.Info("Verifying that the Teleporter Registry was updated") - newVersion, err := cChainInfo.TeleporterRegistry.LatestVersion(&bind.CallOpts{}) - Expect(err).Should(BeNil()) - Expect(newVersion.Cmp(expectedNewVersion)).Should(Equal(0)) + client := http.Client{ + Timeout: 30 * time.Second, + } + + requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, relayer.RelayMessageApiPath) + + // Send request to API + { + b, err := json.Marshal(reqBody) + Expect(err).Should(BeNil()) + bodyReader := bytes.NewReader(b) + + req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader) + Expect(err).Should(BeNil()) + req.Header.Set("Content-Type", "application/json") + + res, err := client.Do(req) + Expect(err).Should(BeNil()) + Expect(res.Status).Should(Equal("200 OK")) + + newVersion, err := cChainInfo.TeleporterRegistry.LatestVersion(&bind.CallOpts{}) + Expect(err).Should(BeNil()) + Expect(newVersion.Cmp(expectedNewVersion)).Should(Equal(0)) + } } From e72a34b93d7b09379b6f893c9f5a059c0034a691 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 20 Jun 2024 12:34:36 -0400 Subject: [PATCH 23/70] lint --- config/config.go | 2 +- relayer/manual_warp_message.go | 2 -- tests/relay_message_api.go | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 relayer/manual_warp_message.go diff --git a/config/config.go b/config/config.go index fc4bc4c5..590c7527 100644 --- a/config/config.go +++ b/config/config.go @@ -118,7 +118,7 @@ func (c *Config) Validate() error { blockchainIDToSubnetID[s.blockchainID] = s.subnetID } c.blockchainIDToSubnetID = blockchainIDToSubnetID - + return nil } diff --git a/relayer/manual_warp_message.go b/relayer/manual_warp_message.go deleted file mode 100644 index 7848e514..00000000 --- a/relayer/manual_warp_message.go +++ /dev/null @@ -1,2 +0,0 @@ -package relayer - diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index b7d34327..b221e090 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -13,6 +13,7 @@ import ( "github.com/ava-labs/awm-relayer/relayer" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" testUtils "github.com/ava-labs/awm-relayer/tests/utils" "github.com/ava-labs/subnet-evm/core/types" subnetEvmInterfaces "github.com/ava-labs/subnet-evm/interfaces" @@ -22,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" . "github.com/onsi/gomega" ) From 6322ac813566b9f1e96940dcfd0b98becbc3b6e2 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 20 Jun 2024 13:34:50 -0400 Subject: [PATCH 24/70] wip --- main/main.go | 1 + relayer/application_relayer.go | 1 + relayer/relay_message_api_handler.go | 4 ++-- tests/e2e_test.go | 2 +- .../{teleporter_registry.go => manual_message.go} | 15 ++++++--------- tests/relay_message_api.go | 2 +- 6 files changed, 12 insertions(+), 13 deletions(-) rename tests/{teleporter_registry.go => manual_message.go} (92%) diff --git a/main/main.go b/main/main.go index 09c98658..7912bddf 100644 --- a/main/main.go +++ b/main/main.go @@ -216,6 +216,7 @@ func main() { // Initialize the API after the message coordinator is set http.Handle("/health", health.NewHandler(checker)) + http.HandleFunc(relayer.RelayApiPath, relayer.RelayAPIHandler) http.HandleFunc(relayer.RelayMessageApiPath, relayer.RelayMessageAPIHandler) // start the health check server diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 1755fd8d..52409b06 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -246,6 +246,7 @@ func (r *ApplicationRelayer) relayMessage( r.logger.Info( "Finished relaying message to destination chain", zap.String("destinationBlockchainID", r.relayerID.DestinationBlockchainID.String()), + zap.String("txHash", txHash.Hex()), ) r.incSuccessfulRelayMessageCount() diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go index 5acb6fda..6c266b30 100644 --- a/relayer/relay_message_api_handler.go +++ b/relayer/relay_message_api_handler.go @@ -35,7 +35,7 @@ type ManualWarpMessage struct { DestinationAddress common.Address } -func RelayAPIHandler(w http.ResponseWriter, r *http.Request) { +func RelayMessageAPIHandler(w http.ResponseWriter, r *http.Request) { var req ManualWarpMessage err := json.NewDecoder(r.Body).Decode(&req) @@ -64,7 +64,7 @@ func RelayAPIHandler(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) } -func RelayMessageAPIHandler(w http.ResponseWriter, r *http.Request) { +func RelayAPIHandler(w http.ResponseWriter, r *http.Request) { var req RelayMessageRequest err := json.NewDecoder(r.Body).Decode(&req) diff --git a/tests/e2e_test.go b/tests/e2e_test.go index b9af3b8f..6dfe4c66 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -62,7 +62,7 @@ var _ = ginkgo.AfterSuite(func() { }) var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { - ginkgo.It("Manually Provided Message", func() { + ginkgo.FIt("Manually Provided Message", func() { ManualMessage(localNetworkInstance) }) ginkgo.It("Basic Relay", func() { diff --git a/tests/teleporter_registry.go b/tests/manual_message.go similarity index 92% rename from tests/teleporter_registry.go rename to tests/manual_message.go index de61079b..549790d9 100644 --- a/tests/teleporter_registry.go +++ b/tests/manual_message.go @@ -17,7 +17,6 @@ import ( "github.com/ava-labs/awm-relayer/relayer" testUtils "github.com/ava-labs/awm-relayer/tests/utils" "github.com/ava-labs/subnet-evm/accounts/abi/bind" - "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/teleporter/tests/interfaces" teleporterTestUtils "github.com/ava-labs/teleporter/tests/utils" "github.com/ethereum/go-ethereum/common" @@ -94,16 +93,14 @@ func ManualMessage(network interfaces.LocalNetwork) { ) relayerConfigPath := testUtils.WriteRelayerConfig(relayerConfig, testUtils.DefaultRelayerCfgFname) - // Subscribe to the destination chain - newHeadsC := make(chan *types.Header, 10) - sub, err := cChainInfo.WSClient.SubscribeNewHead(ctx, newHeadsC) - Expect(err).Should(BeNil()) - defer sub.Unsubscribe() - log.Info("Starting the relayer") relayerCleanup := testUtils.BuildAndRunRelayerExecutable(ctx, relayerConfigPath) defer relayerCleanup() + // Sleep for some time to make sure relayer has started up and subscribed. + log.Info("Waiting for the relayer to start up") + time.Sleep(15 * time.Second) + reqBody := relayer.ManualWarpMessage{ UnsignedMessageBytes: unsignedMessage.Bytes(), SourceBlockchainID: cChainInfo.BlockchainID, @@ -132,8 +129,8 @@ func ManualMessage(network interfaces.LocalNetwork) { Expect(err).Should(BeNil()) Expect(res.Status).Should(Equal("200 OK")) - newVersion, err := cChainInfo.TeleporterRegistry.LatestVersion(&bind.CallOpts{}) + newVersion, err := cChainInfo.TeleporterRegistry.LatesatVersion(&bind.CallOpts{}) Expect(err).Should(BeNil()) - Expect(newVersion.Cmp(expectedNewVersion)).Should(Equal(0)) + Expect(newVersion.Uint64()).Should(Equal(expectedNewVersion.Uint64())) } } diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index b221e090..79a20929 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -75,7 +75,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { Timeout: 30 * time.Second, } - requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, relayer.RelayMessageApiPath) + requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, relayer.RelayApiPath) // Send request to API { From 6ed13b44fcc73c08f548044cf6a3fd201933945f Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 20 Jun 2024 13:53:52 -0400 Subject: [PATCH 25/70] lint --- tests/manual_message.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/manual_message.go b/tests/manual_message.go index 549790d9..a5d05bcf 100644 --- a/tests/manual_message.go +++ b/tests/manual_message.go @@ -129,7 +129,7 @@ func ManualMessage(network interfaces.LocalNetwork) { Expect(err).Should(BeNil()) Expect(res.Status).Should(Equal("200 OK")) - newVersion, err := cChainInfo.TeleporterRegistry.LatesatVersion(&bind.CallOpts{}) + newVersion, err := cChainInfo.TeleporterRegistry.LatestVersion(&bind.CallOpts{}) Expect(err).Should(BeNil()) Expect(newVersion.Uint64()).Should(Equal(expectedNewVersion.Uint64())) } From 358894bdd1b471a011d664f0946c6aa7ed16c66d Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 25 Jun 2024 12:17:17 -0400 Subject: [PATCH 26/70] Pass MessageCoordinator as param --- main/main.go | 7 +- relayer/listener.go | 6 +- relayer/message_coordinator.go | 29 +------ relayer/relay_message_api_handler.go | 114 ++++++++++++++------------- 4 files changed, 71 insertions(+), 85 deletions(-) diff --git a/main/main.go b/main/main.go index 7912bddf..faa272b4 100644 --- a/main/main.go +++ b/main/main.go @@ -212,12 +212,12 @@ func main() { logger.Fatal("Failed to create application relayers", zap.Error(err)) panic(err) } - relayer.SetMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) + messageCoordinator := relayer.NewMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) // Initialize the API after the message coordinator is set http.Handle("/health", health.NewHandler(checker)) - http.HandleFunc(relayer.RelayApiPath, relayer.RelayAPIHandler) - http.HandleFunc(relayer.RelayMessageApiPath, relayer.RelayMessageAPIHandler) + http.Handle(relayer.RelayApiPath, relayer.RelayAPIHandler(messageCoordinator)) + http.Handle(relayer.RelayMessageApiPath, relayer.RelayMessageAPIHandler(messageCoordinator)) // start the health check server go func() { @@ -243,6 +243,7 @@ func main() { isHealthy, cfg.ProcessMissedBlocks, minHeights[sourceBlockchain.GetBlockchainID()], + messageCoordinator, ) }) } diff --git a/relayer/listener.go b/relayer/listener.go index da722248..0924f2f4 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -36,6 +36,7 @@ type Listener struct { catchUpResultChan chan bool healthStatus *atomic.Bool ethClient ethclient.Client + messageCoordinator *MessageCoordinator } // runListener creates a Listener instance and the ApplicationRelayers for a subnet. @@ -48,6 +49,7 @@ func RunListener( relayerHealth *atomic.Bool, processMissedBlocks bool, minHeight uint64, + messageCoordinator *MessageCoordinator, ) error { // Create the Listener listener, err := newListener( @@ -58,6 +60,7 @@ func RunListener( relayerHealth, processMissedBlocks, minHeight, + messageCoordinator, ) if err != nil { return fmt.Errorf("failed to create listener instance: %w", err) @@ -81,6 +84,7 @@ func newListener( relayerHealth *atomic.Bool, processMissedBlocks bool, startingHeight uint64, + messageCoordinator *MessageCoordinator, ) (*Listener, error) { blockchainID, err := ids.FromString(sourceBlockchain.BlockchainID) if err != nil { @@ -197,7 +201,7 @@ func (lstnr *Listener) processLogs(ctx context.Context) error { return fmt.Errorf("failed to catch up on historical blocks") } case blockHeader := <-lstnr.Subscriber.Headers(): - go ProcessBlock(blockHeader, lstnr.ethClient, errChan) + go lstnr.messageCoordinator.processBlock(blockHeader, lstnr.ethClient, errChan) case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) lstnr.logger.Error( diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index d9d71eb6..c020fd99 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -19,8 +19,6 @@ import ( "go.uber.org/zap" ) -var globalMessageCoordinator *MessageCoordinator - type MessageCoordinator struct { logger logging.Logger // Maps Source blockchain ID and protocol address to a Message Handler Factory @@ -29,13 +27,13 @@ type MessageCoordinator struct { SourceClients map[ids.ID]ethclient.Client } -func SetMessageCoordinator( +func NewMessageCoordinator( logger logging.Logger, messageHandlerFactories map[ids.ID]map[common.Address]messages.MessageHandlerFactory, applicationRelayers map[common.Hash]*ApplicationRelayer, sourceClients map[ids.ID]ethclient.Client, -) { - globalMessageCoordinator = &MessageCoordinator{ +) *MessageCoordinator { + return &MessageCoordinator{ logger: logger, MessageHandlerFactories: messageHandlerFactories, ApplicationRelayers: applicationRelayers, @@ -164,13 +162,6 @@ func (mc *MessageCoordinator) getApplicationRelayer( return nil } -func ProcessManualWarpMessage(warpMessage *relayerTypes.WarpMessageInfo) (common.Hash, error) { - if globalMessageCoordinator == nil { - return common.Hash{}, fmt.Errorf("global message coordinator not set") - } - return globalMessageCoordinator.processManualWarpMessage(warpMessage) -} - func (mc *MessageCoordinator) processManualWarpMessage( warpMessage *relayerTypes.WarpMessageInfo, ) (common.Hash, error) { @@ -193,13 +184,6 @@ func (mc *MessageCoordinator) processManualWarpMessage( return appRelayer.ProcessMessage(handler) } -func ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { - if globalMessageCoordinator == nil { - panic("global message coordinator not set") - } - return globalMessageCoordinator.processMessage(blockchainID, messageID, blockNum) -} - func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { ethClient, ok := mc.SourceClients[blockchainID] if !ok { @@ -230,13 +214,6 @@ func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID comm return appRelayer.ProcessMessage(handler) } -func ProcessBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { - if globalMessageCoordinator == nil { - panic("global message coordinator not set") - } - globalMessageCoordinator.processBlock(blockHeader, ethClient, errChan) -} - // Meant to be ran asynchronously. Errors should be sent to errChan. func (mc *MessageCoordinator) processBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { // Parse the logs in the block, and group by application relayer diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go index 6c266b30..1d7f155d 100644 --- a/relayer/relay_message_api_handler.go +++ b/relayer/relay_message_api_handler.go @@ -35,61 +35,65 @@ type ManualWarpMessage struct { DestinationAddress common.Address } -func RelayMessageAPIHandler(w http.ResponseWriter, r *http.Request) { - var req ManualWarpMessage - - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - unsignedMessage, err := types.UnpackWarpMessage(req.UnsignedMessageBytes) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - warpMessageInfo := &relayerTypes.WarpMessageInfo{ - SourceAddress: req.SourceAddress, - UnsignedMessage: unsignedMessage, - } - - txHash, err := ProcessManualWarpMessage(warpMessageInfo) - if err != nil { - http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) - return - } - - _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) +func RelayMessageAPIHandler(messageCoordinator *MessageCoordinator) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var req ManualWarpMessage + + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + unsignedMessage, err := types.UnpackWarpMessage(req.UnsignedMessageBytes) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + warpMessageInfo := &relayerTypes.WarpMessageInfo{ + SourceAddress: req.SourceAddress, + UnsignedMessage: unsignedMessage, + } + + txHash, err := messageCoordinator.processManualWarpMessage(warpMessageInfo) + if err != nil { + http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) + }) } -func RelayAPIHandler(w http.ResponseWriter, r *http.Request) { - var req RelayMessageRequest - - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - blockchainID, err := ids.FromString(req.BlockchainID) - if err != nil { - http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) - return - } - messageID := common.HexToHash(req.MessageID) - blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) - if !ok { - http.Error(w, "invalid blockNum", http.StatusBadRequest) - return - } - - txHash, err := ProcessMessage(blockchainID, messageID, blockNum) - if err != nil { - http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) - return - } - - _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) +func RelayAPIHandler(messageCoordinator *MessageCoordinator) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var req RelayMessageRequest + + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + blockchainID, err := ids.FromString(req.BlockchainID) + if err != nil { + http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) + return + } + messageID := common.HexToHash(req.MessageID) + blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) + if !ok { + http.Error(w, "invalid blockNum", http.StatusBadRequest) + return + } + + txHash, err := messageCoordinator.processMessage(blockchainID, messageID, blockNum) + if err != nil { + http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) + }) } From 3a67da1fced5014df3bb56adcae3760002bc900c Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 25 Jun 2024 12:17:57 -0400 Subject: [PATCH 27/70] Lint --- relayer/listener.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/relayer/listener.go b/relayer/listener.go index 0924f2f4..4ea3bd1f 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -28,14 +28,14 @@ const ( // Listener handles all messages sent from a given source chain type Listener struct { - Subscriber vms.Subscriber - currentRequestID uint32 - contractMessage vms.ContractMessage - logger logging.Logger - sourceBlockchain config.SourceBlockchain - catchUpResultChan chan bool - healthStatus *atomic.Bool - ethClient ethclient.Client + Subscriber vms.Subscriber + currentRequestID uint32 + contractMessage vms.ContractMessage + logger logging.Logger + sourceBlockchain config.SourceBlockchain + catchUpResultChan chan bool + healthStatus *atomic.Bool + ethClient ethclient.Client messageCoordinator *MessageCoordinator } From 53516b5584defa9c337bfb656ae62407b032ad4d Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 25 Jun 2024 13:02:16 -0400 Subject: [PATCH 28/70] Fix --- relayer/listener.go | 17 +++++++++-------- tests/e2e_test.go | 2 +- tests/manual_message.go | 3 +++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/relayer/listener.go b/relayer/listener.go index 4ea3bd1f..d10fee59 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -126,14 +126,15 @@ func newListener( zap.String("blockchainIDHex", sourceBlockchain.GetBlockchainID().Hex()), ) lstnr := Listener{ - Subscriber: sub, - currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision - contractMessage: vms.NewContractMessage(logger, sourceBlockchain), - logger: logger, - sourceBlockchain: sourceBlockchain, - catchUpResultChan: catchUpResultChan, - healthStatus: relayerHealth, - ethClient: ethRPCClient, + Subscriber: sub, + currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision + contractMessage: vms.NewContractMessage(logger, sourceBlockchain), + logger: logger, + sourceBlockchain: sourceBlockchain, + catchUpResultChan: catchUpResultChan, + healthStatus: relayerHealth, + ethClient: ethRPCClient, + messageCoordinator: messageCoordinator, } // Open the subscription. We must do this before processing any missed messages, otherwise we may miss an incoming message diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 6dfe4c66..b9af3b8f 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -62,7 +62,7 @@ var _ = ginkgo.AfterSuite(func() { }) var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { - ginkgo.FIt("Manually Provided Message", func() { + ginkgo.It("Manually Provided Message", func() { ManualMessage(localNetworkInstance) }) ginkgo.It("Basic Relay", func() { diff --git a/tests/manual_message.go b/tests/manual_message.go index a5d05bcf..56759606 100644 --- a/tests/manual_message.go +++ b/tests/manual_message.go @@ -129,6 +129,9 @@ func ManualMessage(network interfaces.LocalNetwork) { Expect(err).Should(BeNil()) Expect(res.Status).Should(Equal("200 OK")) + // Wait for all nodes to see new transaction + time.Sleep(1 * time.Second) + newVersion, err := cChainInfo.TeleporterRegistry.LatestVersion(&bind.CallOpts{}) Expect(err).Should(BeNil()) Expect(newVersion.Uint64()).Should(Equal(expectedNewVersion.Uint64())) From 93e9a751911621d470c91f72847358873cef8dbc Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 25 Jun 2024 15:05:12 -0400 Subject: [PATCH 29/70] Refactor API --- main/main.go | 49 +++++--------- relayer/listener.go | 2 +- relayer/message_coordinator.go | 6 +- relayer/relay_message_api_handler.go | 99 ---------------------------- tests/manual_message.go | 6 +- tests/relay_message_api.go | 7 +- 6 files changed, 26 insertions(+), 143 deletions(-) delete mode 100644 relayer/relay_message_api_handler.go diff --git a/main/main.go b/main/main.go index faa272b4..870c277f 100644 --- a/main/main.go +++ b/main/main.go @@ -15,12 +15,12 @@ import ( offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" "github.com/ava-labs/awm-relayer/messages/teleporter" - "github.com/alexliesenfeld/health" "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/awm-relayer/api" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/database" "github.com/ava-labs/awm-relayer/peers" @@ -124,7 +124,6 @@ func main() { // Initialize the global app request network logger.Info("Initializing app request network") - // The app request network generates P2P networking logs that are verbose at the info level. // Unless the log level is debug or lower, set the network log level to error to avoid spamming the logs. networkLogLevel := logging.Error @@ -141,29 +140,6 @@ func main() { panic(err) } - // Each goroutine will have an atomic bool that it can set to false if it ever disconnects from its subscription. - relayerHealth := make(map[ids.ID]*atomic.Bool) - - checker := health.NewChecker( - health.WithCheck(health.Check{ - Name: "relayers-all", - Check: func(context.Context) error { - // Store the IDs as the cb58 encoding - var unhealthyRelayers []string - for id, health := range relayerHealth { - if !health.Load() { - unhealthyRelayers = append(unhealthyRelayers, id.String()) - } - } - - if len(unhealthyRelayers) > 0 { - return fmt.Errorf("relayers are unhealthy for blockchains %v", unhealthyRelayers) - } - return nil - }, - }), - ) - startMetricsServer(logger, gatherer, cfg.MetricsPort) relayerMetrics, err := relayer.NewApplicationRelayerMetrics(registerer) @@ -190,6 +166,8 @@ func main() { ticker := utils.NewTicker(cfg.DBWriteIntervalSeconds) go ticker.Run() + relayerHealth := createHealthTrackers(&cfg) + messageHandlerFactories, err := createMessageHandlerFactories(logger, &cfg) if err != nil { logger.Fatal("Failed to create message handler factories", zap.Error(err)) @@ -214,10 +192,10 @@ func main() { } messageCoordinator := relayer.NewMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) - // Initialize the API after the message coordinator is set - http.Handle("/health", health.NewHandler(checker)) - http.Handle(relayer.RelayApiPath, relayer.RelayAPIHandler(messageCoordinator)) - http.Handle(relayer.RelayMessageApiPath, relayer.RelayMessageAPIHandler(messageCoordinator)) + // Each goroutine will have an atomic bool that it can set to false if it ever disconnects from its subscription. + api.HandleHealthCheck(relayerHealth) + api.HandleRelay(messageCoordinator) + api.HandleRelayMessage(messageCoordinator) // start the health check server go func() { @@ -229,9 +207,6 @@ func main() { for _, s := range cfg.SourceBlockchains { sourceBlockchain := s - isHealthy := atomic.NewBool(true) - relayerHealth[s.GetBlockchainID()] = isHealthy - // errgroup will cancel the context when the first goroutine returns an error errGroup.Go(func() error { // runListener runs until it errors or the context is cancelled by another goroutine @@ -240,7 +215,7 @@ func main() { logger, *sourceBlockchain, sourceClients[sourceBlockchain.GetBlockchainID()], - isHealthy, + relayerHealth[sourceBlockchain.GetBlockchainID()], cfg.ProcessMissedBlocks, minHeights[sourceBlockchain.GetBlockchainID()], messageCoordinator, @@ -446,6 +421,14 @@ func createApplicationRelayersForSourceChain( return applicationRelayers, minHeight, nil } +func createHealthTrackers(cfg *config.Config) map[ids.ID]*atomic.Bool { + healthTrackers := make(map[ids.ID]*atomic.Bool, len(cfg.SourceBlockchains)) + for _, sourceBlockchain := range cfg.SourceBlockchains { + healthTrackers[sourceBlockchain.GetBlockchainID()] = atomic.NewBool(true) + } + return healthTrackers +} + func startMetricsServer(logger logging.Logger, gatherer prometheus.Gatherer, port uint16) { http.Handle("/metrics", promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{})) diff --git a/relayer/listener.go b/relayer/listener.go index d10fee59..b3eb73ed 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -202,7 +202,7 @@ func (lstnr *Listener) processLogs(ctx context.Context) error { return fmt.Errorf("failed to catch up on historical blocks") } case blockHeader := <-lstnr.Subscriber.Headers(): - go lstnr.messageCoordinator.processBlock(blockHeader, lstnr.ethClient, errChan) + go lstnr.messageCoordinator.ProcessBlock(blockHeader, lstnr.ethClient, errChan) case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) lstnr.logger.Error( diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index c020fd99..db90795f 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -162,7 +162,7 @@ func (mc *MessageCoordinator) getApplicationRelayer( return nil } -func (mc *MessageCoordinator) processManualWarpMessage( +func (mc *MessageCoordinator) ProcessManualWarpMessage( warpMessage *relayerTypes.WarpMessageInfo, ) (common.Hash, error) { // Send any messages that were specified in the configuration @@ -184,7 +184,7 @@ func (mc *MessageCoordinator) processManualWarpMessage( return appRelayer.ProcessMessage(handler) } -func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { +func (mc *MessageCoordinator) ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { ethClient, ok := mc.SourceClients[blockchainID] if !ok { mc.logger.Error("Source client not found", zap.String("blockchainID", blockchainID.String())) @@ -215,7 +215,7 @@ func (mc *MessageCoordinator) processMessage(blockchainID ids.ID, messageID comm } // Meant to be ran asynchronously. Errors should be sent to errChan. -func (mc *MessageCoordinator) processBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { +func (mc *MessageCoordinator) ProcessBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { // Parse the logs in the block, and group by application relayer block, err := relayerTypes.NewWarpBlockInfo(blockHeader, ethClient) if err != nil { diff --git a/relayer/relay_message_api_handler.go b/relayer/relay_message_api_handler.go deleted file mode 100644 index 1d7f155d..00000000 --- a/relayer/relay_message_api_handler.go +++ /dev/null @@ -1,99 +0,0 @@ -package relayer - -import ( - "encoding/json" - "math/big" - "net/http" - - "github.com/ava-labs/awm-relayer/types" - relayerTypes "github.com/ava-labs/awm-relayer/types" - - "github.com/ava-labs/avalanchego/ids" - "github.com/ethereum/go-ethereum/common" -) - -const ( - RelayApiPath = "/relay" - RelayMessageApiPath = RelayApiPath + "/message" -) - -type RelayMessageRequest struct { - // Required. cb58 encoding of the blockchain ID - BlockchainID string `json:"blockchain-id"` - // Required. Hex encoding of the warp message ID - MessageID string `json:"message-id"` - // Required. Integer representation of the block number - BlockNum string `json:"block-num"` -} - -// Defines a manual warp message to be sent from the relayer through the API. -type ManualWarpMessage struct { - UnsignedMessageBytes []byte - SourceBlockchainID ids.ID - DestinationBlockchainID ids.ID - SourceAddress common.Address - DestinationAddress common.Address -} - -func RelayMessageAPIHandler(messageCoordinator *MessageCoordinator) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var req ManualWarpMessage - - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - unsignedMessage, err := types.UnpackWarpMessage(req.UnsignedMessageBytes) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - warpMessageInfo := &relayerTypes.WarpMessageInfo{ - SourceAddress: req.SourceAddress, - UnsignedMessage: unsignedMessage, - } - - txHash, err := messageCoordinator.processManualWarpMessage(warpMessageInfo) - if err != nil { - http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) - return - } - - _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) - }) -} - -func RelayAPIHandler(messageCoordinator *MessageCoordinator) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var req RelayMessageRequest - - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - blockchainID, err := ids.FromString(req.BlockchainID) - if err != nil { - http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) - return - } - messageID := common.HexToHash(req.MessageID) - blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) - if !ok { - http.Error(w, "invalid blockNum", http.StatusBadRequest) - return - } - - txHash, err := messageCoordinator.processMessage(blockchainID, messageID, blockNum) - if err != nil { - http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) - return - } - - _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) - }) -} diff --git a/tests/manual_message.go b/tests/manual_message.go index 56759606..92fd2e40 100644 --- a/tests/manual_message.go +++ b/tests/manual_message.go @@ -13,8 +13,8 @@ import ( "time" runner_sdk "github.com/ava-labs/avalanche-network-runner/client" + "github.com/ava-labs/awm-relayer/api" offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" - "github.com/ava-labs/awm-relayer/relayer" testUtils "github.com/ava-labs/awm-relayer/tests/utils" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/teleporter/tests/interfaces" @@ -101,7 +101,7 @@ func ManualMessage(network interfaces.LocalNetwork) { log.Info("Waiting for the relayer to start up") time.Sleep(15 * time.Second) - reqBody := relayer.ManualWarpMessage{ + reqBody := api.ManualWarpMessage{ UnsignedMessageBytes: unsignedMessage.Bytes(), SourceBlockchainID: cChainInfo.BlockchainID, DestinationBlockchainID: cChainInfo.BlockchainID, @@ -113,7 +113,7 @@ func ManualMessage(network interfaces.LocalNetwork) { Timeout: 30 * time.Second, } - requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, relayer.RelayMessageApiPath) + requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, api.RelayMessageApiPath) // Send request to API { diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index 79a20929..6483531e 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -11,9 +11,8 @@ import ( "net/http" "time" - "github.com/ava-labs/awm-relayer/relayer" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/awm-relayer/api" testUtils "github.com/ava-labs/awm-relayer/tests/utils" "github.com/ava-labs/subnet-evm/core/types" subnetEvmInterfaces "github.com/ava-labs/subnet-evm/interfaces" @@ -65,7 +64,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { log.Info("Waiting for the relayer to start up") time.Sleep(15 * time.Second) - reqBody := relayer.RelayMessageRequest{ + reqBody := api.RelayMessageRequest{ BlockchainID: subnetAInfo.BlockchainID.String(), MessageID: warpMessage.ID().Hex(), BlockNum: receipt.BlockNumber.String(), @@ -75,7 +74,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { Timeout: 30 * time.Second, } - requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, relayer.RelayApiPath) + requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, api.RelayApiPath) // Send request to API { From daac2cdfe3203058a58b9ee00e124c41367d0ef8 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 25 Jun 2024 15:07:16 -0400 Subject: [PATCH 30/70] Add api folder --- api/health_check.go | 39 ++++++++++++++++ api/relay_message.go | 108 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 api/health_check.go create mode 100644 api/relay_message.go diff --git a/api/health_check.go b/api/health_check.go new file mode 100644 index 00000000..10feaf56 --- /dev/null +++ b/api/health_check.go @@ -0,0 +1,39 @@ +package api + +import ( + "context" + "fmt" + "net/http" + + "github.com/alexliesenfeld/health" + "github.com/ava-labs/avalanchego/ids" + "go.uber.org/atomic" +) + +const HealthAPIPath = "/health" + +func HandleHealthCheck(relayerHealth map[ids.ID]*atomic.Bool) { + http.Handle(HealthAPIPath, healthCheckHandler(relayerHealth)) +} + +func healthCheckHandler(relayerHealth map[ids.ID]*atomic.Bool) http.Handler { + return health.NewHandler(health.NewChecker( + health.WithCheck(health.Check{ + Name: "relayers-all", + Check: func(context.Context) error { + // Store the IDs as the cb58 encoding + var unhealthyRelayers []string + for id, health := range relayerHealth { + if !health.Load() { + unhealthyRelayers = append(unhealthyRelayers, id.String()) + } + } + + if len(unhealthyRelayers) > 0 { + return fmt.Errorf("relayers are unhealthy for blockchains %v", unhealthyRelayers) + } + return nil + }, + }), + )) +} diff --git a/api/relay_message.go b/api/relay_message.go new file mode 100644 index 00000000..4aa0570c --- /dev/null +++ b/api/relay_message.go @@ -0,0 +1,108 @@ +package api + +import ( + "encoding/json" + "math/big" + "net/http" + + "github.com/ava-labs/awm-relayer/relayer" + "github.com/ava-labs/awm-relayer/types" + relayerTypes "github.com/ava-labs/awm-relayer/types" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ethereum/go-ethereum/common" +) + +const ( + RelayApiPath = "/relay" + RelayMessageApiPath = RelayApiPath + "/message" +) + +type RelayMessageRequest struct { + // Required. cb58 encoding of the blockchain ID + BlockchainID string `json:"blockchain-id"` + // Required. Hex encoding of the warp message ID + MessageID string `json:"message-id"` + // Required. Integer representation of the block number + BlockNum string `json:"block-num"` +} + +// Defines a manual warp message to be sent from the relayer through the API. +type ManualWarpMessage struct { + UnsignedMessageBytes []byte + SourceBlockchainID ids.ID + DestinationBlockchainID ids.ID + SourceAddress common.Address + DestinationAddress common.Address +} + +func HandleRelayMessage(messageCoordinator *relayer.MessageCoordinator) { + http.Handle(RelayApiPath, relayAPIHandler(messageCoordinator)) +} + +func HandleRelay(messageCoordinator *relayer.MessageCoordinator) { + http.Handle(RelayMessageApiPath, relayMessageAPIHandler(messageCoordinator)) +} + +func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var req ManualWarpMessage + + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + unsignedMessage, err := types.UnpackWarpMessage(req.UnsignedMessageBytes) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + warpMessageInfo := &relayerTypes.WarpMessageInfo{ + SourceAddress: req.SourceAddress, + UnsignedMessage: unsignedMessage, + } + + txHash, err := messageCoordinator.ProcessManualWarpMessage(warpMessageInfo) + if err != nil { + http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) + }) +} + +func relayAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var req RelayMessageRequest + + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + blockchainID, err := ids.FromString(req.BlockchainID) + if err != nil { + http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) + return + } + messageID := common.HexToHash(req.MessageID) + blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) + if !ok { + http.Error(w, "invalid blockNum", http.StatusBadRequest) + return + } + + txHash, err := messageCoordinator.ProcessMessage(blockchainID, messageID, blockNum) + if err != nil { + http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) + }) +} From 45657f7ea105a66ae79b67b894f354e7ac8b744e Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 25 Jun 2024 16:48:51 -0400 Subject: [PATCH 31/70] Make message coordinator fields private --- relayer/message_coordinator.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index db90795f..73f76000 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -22,9 +22,9 @@ import ( type MessageCoordinator struct { logger logging.Logger // Maps Source blockchain ID and protocol address to a Message Handler Factory - MessageHandlerFactories map[ids.ID]map[common.Address]messages.MessageHandlerFactory - ApplicationRelayers map[common.Hash]*ApplicationRelayer - SourceClients map[ids.ID]ethclient.Client + messageHandlerFactories map[ids.ID]map[common.Address]messages.MessageHandlerFactory + applicationRelayers map[common.Hash]*ApplicationRelayer + sourceClients map[ids.ID]ethclient.Client } func NewMessageCoordinator( @@ -35,9 +35,9 @@ func NewMessageCoordinator( ) *MessageCoordinator { return &MessageCoordinator{ logger: logger, - MessageHandlerFactories: messageHandlerFactories, - ApplicationRelayers: applicationRelayers, - SourceClients: sourceClients, + messageHandlerFactories: messageHandlerFactories, + applicationRelayers: applicationRelayers, + sourceClients: sourceClients, } } @@ -53,7 +53,7 @@ func (mc *MessageCoordinator) GetAppRelayerMessageHandler( error, ) { // Check that the warp message is from a supported message protocol contract address. - messageHandlerFactory, supportedMessageProtocol := mc.MessageHandlerFactories[warpMessageInfo.UnsignedMessage.SourceChainID][warpMessageInfo.SourceAddress] + messageHandlerFactory, supportedMessageProtocol := mc.messageHandlerFactories[warpMessageInfo.UnsignedMessage.SourceChainID][warpMessageInfo.SourceAddress] if !supportedMessageProtocol { // Do not return an error here because it is expected for there to be messages from other contracts // than just the ones supported by a single listener instance. @@ -116,7 +116,7 @@ func (mc *MessageCoordinator) getApplicationRelayer( originSenderAddress, destinationAddress, ) - if applicationRelayer, ok := mc.ApplicationRelayers[applicationRelayerID]; ok { + if applicationRelayer, ok := mc.applicationRelayers[applicationRelayerID]; ok { return applicationRelayer } @@ -127,7 +127,7 @@ func (mc *MessageCoordinator) getApplicationRelayer( originSenderAddress, database.AllAllowedAddress, ) - if applicationRelayer, ok := mc.ApplicationRelayers[applicationRelayerID]; ok { + if applicationRelayer, ok := mc.applicationRelayers[applicationRelayerID]; ok { return applicationRelayer } @@ -138,7 +138,7 @@ func (mc *MessageCoordinator) getApplicationRelayer( database.AllAllowedAddress, destinationAddress, ) - if applicationRelayer, ok := mc.ApplicationRelayers[applicationRelayerID]; ok { + if applicationRelayer, ok := mc.applicationRelayers[applicationRelayerID]; ok { return applicationRelayer } @@ -149,7 +149,7 @@ func (mc *MessageCoordinator) getApplicationRelayer( database.AllAllowedAddress, database.AllAllowedAddress, ) - if applicationRelayer, ok := mc.ApplicationRelayers[applicationRelayerID]; ok { + if applicationRelayer, ok := mc.applicationRelayers[applicationRelayerID]; ok { return applicationRelayer } mc.logger.Debug( @@ -185,7 +185,7 @@ func (mc *MessageCoordinator) ProcessManualWarpMessage( } func (mc *MessageCoordinator) ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { - ethClient, ok := mc.SourceClients[blockchainID] + ethClient, ok := mc.sourceClients[blockchainID] if !ok { mc.logger.Error("Source client not found", zap.String("blockchainID", blockchainID.String())) return common.Hash{}, fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) @@ -244,7 +244,7 @@ func (mc *MessageCoordinator) ProcessBlock(blockHeader *types.Header, ethClient messageHandlers[appRelayer.relayerID.ID] = append(messageHandlers[appRelayer.relayerID.ID], handler) } // Initiate message relay of all registered messages - for _, appRelayer := range mc.ApplicationRelayers { + for _, appRelayer := range mc.applicationRelayers { // Dispatch all messages in the block to the appropriate application relayer. // An empty slice is still a valid argument to ProcessHeight; in this case the height is immediately committed. handlers := messageHandlers[appRelayer.relayerID.ID] From a77d2449502fa586e857b741bf5fb8b5d9329d6e Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 26 Jun 2024 12:29:42 -0400 Subject: [PATCH 32/70] lint --- relayer/application_relayer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index abb3d3a9..9c6c36d4 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -30,8 +30,8 @@ import ( "github.com/ava-labs/awm-relayer/vms" coreEthMsg "github.com/ava-labs/coreth/plugin/evm/message" msg "github.com/ava-labs/subnet-evm/plugin/evm/message" - "github.com/ethereum/go-ethereum/common" "github.com/ava-labs/subnet-evm/rpc" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "golang.org/x/sync/errgroup" From 14810a27805598dffd99386bf8df06b3af3b1395 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 26 Jun 2024 13:10:03 -0400 Subject: [PATCH 33/70] Remove unneeded fields --- api/relay_message.go | 11 ++++------- tests/manual_message.go | 9 +++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/api/relay_message.go b/api/relay_message.go index 4aa0570c..73448355 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -28,12 +28,9 @@ type RelayMessageRequest struct { } // Defines a manual warp message to be sent from the relayer through the API. -type ManualWarpMessage struct { - UnsignedMessageBytes []byte - SourceBlockchainID ids.ID - DestinationBlockchainID ids.ID - SourceAddress common.Address - DestinationAddress common.Address +type ManualWarpMessageRequest struct { + UnsignedMessageBytes []byte + SourceAddress common.Address } func HandleRelayMessage(messageCoordinator *relayer.MessageCoordinator) { @@ -46,7 +43,7 @@ func HandleRelay(messageCoordinator *relayer.MessageCoordinator) { func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var req ManualWarpMessage + var req ManualWarpMessageRequest err := json.NewDecoder(r.Body).Decode(&req) if err != nil { diff --git a/tests/manual_message.go b/tests/manual_message.go index 92fd2e40..16eff7ad 100644 --- a/tests/manual_message.go +++ b/tests/manual_message.go @@ -101,12 +101,9 @@ func ManualMessage(network interfaces.LocalNetwork) { log.Info("Waiting for the relayer to start up") time.Sleep(15 * time.Second) - reqBody := api.ManualWarpMessage{ - UnsignedMessageBytes: unsignedMessage.Bytes(), - SourceBlockchainID: cChainInfo.BlockchainID, - DestinationBlockchainID: cChainInfo.BlockchainID, - SourceAddress: offchainregistry.OffChainRegistrySourceAddress, - DestinationAddress: cChainInfo.TeleporterRegistryAddress, + reqBody := api.ManualWarpMessageRequest{ + UnsignedMessageBytes: unsignedMessage.Bytes(), + SourceAddress: offchainregistry.OffChainRegistrySourceAddress, } client := http.Client{ From 59ec4d4c3abcbd8a84d1e8eeff881f512ca4227a Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 26 Jun 2024 15:58:10 -0400 Subject: [PATCH 34/70] Update main/main.go Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> Signed-off-by: Geoff Stuart --- main/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.go b/main/main.go index 0f5e116d..5b813ceb 100644 --- a/main/main.go +++ b/main/main.go @@ -200,7 +200,7 @@ func main() { } messageCoordinator := relayer.NewMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) - // Each goroutine will have an atomic bool that it can set to false if it ever disconnects from its subscription. + // Each Listener goroutine will have an atomic bool that it can set to false to indicate an unrecoverable error api.HandleHealthCheck(relayerHealth) api.HandleRelay(messageCoordinator) api.HandleRelayMessage(messageCoordinator) From 214abb8e833db8f615382b2f5f8a47adca80f7b8 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 26 Jun 2024 15:58:16 -0400 Subject: [PATCH 35/70] Update main/main.go Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> Signed-off-by: Geoff Stuart --- main/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/main.go b/main/main.go index 5b813ceb..6d3105c4 100644 --- a/main/main.go +++ b/main/main.go @@ -241,7 +241,7 @@ func createMessageHandlerFactories( messageHandlerFactories := make(map[ids.ID]map[common.Address]messages.MessageHandlerFactory) for _, sourceBlockchain := range globalConfig.SourceBlockchains { messageHandlerFactoriesForSource := make(map[common.Address]messages.MessageHandlerFactory) - // Create message managers for each supported message protocol + // Create message handler factories for each supported message protocol for addressStr, cfg := range sourceBlockchain.MessageContracts { address := common.HexToAddress(addressStr) format := cfg.MessageFormat From a39bdcff48239ec566eb95f29f6987ea7604b43f Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 10:17:40 -0400 Subject: [PATCH 36/70] Update relayer/message_coordinator.go Co-authored-by: Michael Kaplan <55204436+michaelkaplan13@users.noreply.github.com> Signed-off-by: Geoff Stuart --- relayer/message_coordinator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 73f76000..197a250a 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -41,7 +41,7 @@ func NewMessageCoordinator( } } -// GetAppRelayerMessageHandler Returns the ApplicationRelayer that is configured to handle this message, as well as a +// GetAppRelayerMessageHandler returns the ApplicationRelayer that is configured to handle this message, as well as a // one-time MessageHandler instance that the ApplicationRelayer uses to relay this specific message. // The MessageHandler and ApplicationRelayer are decoupled to support batch workflows in which a single ApplicationRelayer // processes multiple messages (using their corresponding MessageHandlers) in a single shot. From 74fd9025379810cc5822f4c5c93ff64c14cf80b7 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 10:17:56 -0400 Subject: [PATCH 37/70] Update api/relay_message.go Co-authored-by: Michael Kaplan <55204436+michaelkaplan13@users.noreply.github.com> Signed-off-by: Geoff Stuart --- api/relay_message.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/relay_message.go b/api/relay_message.go index 73448355..177226d3 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -19,7 +19,7 @@ const ( ) type RelayMessageRequest struct { - // Required. cb58 encoding of the blockchain ID + // Required. cb58 encoding of the source blockchain ID for the message BlockchainID string `json:"blockchain-id"` // Required. Hex encoding of the warp message ID MessageID string `json:"message-id"` From cdd563539a782a8fabffb66cd1814e2fc01ef8a2 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 10:18:11 -0400 Subject: [PATCH 38/70] Update api/relay_message.go Co-authored-by: Michael Kaplan <55204436+michaelkaplan13@users.noreply.github.com> Signed-off-by: Geoff Stuart --- api/relay_message.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/relay_message.go b/api/relay_message.go index 177226d3..b8c2217d 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -23,7 +23,7 @@ type RelayMessageRequest struct { BlockchainID string `json:"blockchain-id"` // Required. Hex encoding of the warp message ID MessageID string `json:"message-id"` - // Required. Integer representation of the block number + // Required. Integer representation of the block number that the message was sent in BlockNum string `json:"block-num"` } From f6e8e9cef1c3313b7dd9fb46c7bd78953930e85d Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 10:25:14 -0400 Subject: [PATCH 39/70] Review fixes --- api/relay_message.go | 8 ++++---- tests/basic_relay.go | 8 ++++++-- tests/manual_message.go | 2 +- tests/relay_message_api.go | 2 +- tests/utils/utils.go | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/api/relay_message.go b/api/relay_message.go index b8c2217d..3cc0ba7f 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -14,8 +14,8 @@ import ( ) const ( - RelayApiPath = "/relay" - RelayMessageApiPath = RelayApiPath + "/message" + RelayAPIPath = "/relay" + RelayMessageAPIPath = RelayAPIPath + "/message" ) type RelayMessageRequest struct { @@ -34,11 +34,11 @@ type ManualWarpMessageRequest struct { } func HandleRelayMessage(messageCoordinator *relayer.MessageCoordinator) { - http.Handle(RelayApiPath, relayAPIHandler(messageCoordinator)) + http.Handle(RelayAPIPath, relayAPIHandler(messageCoordinator)) } func HandleRelay(messageCoordinator *relayer.MessageCoordinator) { - http.Handle(RelayMessageApiPath, relayMessageAPIHandler(messageCoordinator)) + http.Handle(RelayMessageAPIPath, relayMessageAPIHandler(messageCoordinator)) } func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handler { diff --git a/tests/basic_relay.go b/tests/basic_relay.go index 2be9c9bb..28bd9735 100644 --- a/tests/basic_relay.go +++ b/tests/basic_relay.go @@ -52,6 +52,8 @@ func BasicRelay(network interfaces.LocalNetwork) { fundedAddress, relayerKey, ) + // The config needs to be validated in order to be passed to database.GetConfigRelayerIDs + relayerConfig.Validate() relayerConfigPath := testUtils.WriteRelayerConfig(relayerConfig, testUtils.DefaultRelayerCfgFname) @@ -114,8 +116,10 @@ func BasicRelay(network interfaces.LocalNetwork) { relayerIDA := database.CalculateRelayerID(subnetAInfo.BlockchainID, subnetBInfo.BlockchainID, database.AllAllowedAddress, database.AllAllowedAddress) relayerIDB := database.CalculateRelayerID(subnetBInfo.BlockchainID, subnetAInfo.BlockchainID, database.AllAllowedAddress, database.AllAllowedAddress) // Modify the JSON database to force the relayer to re-process old blocks - _ = jsonDB.Put(relayerIDA, database.LatestProcessedBlockKey, []byte("0")) - _ = jsonDB.Put(relayerIDB, database.LatestProcessedBlockKey, []byte("0")) + err = jsonDB.Put(relayerIDA, database.LatestProcessedBlockKey, []byte("0")) + Expect(err).Should(BeNil()) + err = jsonDB.Put(relayerIDB, database.LatestProcessedBlockKey, []byte("0")) + Expect(err).Should(BeNil()) // Subscribe to the destination chain newHeadsB := make(chan *types.Header, 10) diff --git a/tests/manual_message.go b/tests/manual_message.go index 16eff7ad..53452dfc 100644 --- a/tests/manual_message.go +++ b/tests/manual_message.go @@ -110,7 +110,7 @@ func ManualMessage(network interfaces.LocalNetwork) { Timeout: 30 * time.Second, } - requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, api.RelayMessageApiPath) + requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, api.RelayMessageAPIPath) // Send request to API { diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index 6483531e..d71342a9 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -74,7 +74,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { Timeout: 30 * time.Second, } - requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, api.RelayApiPath) + requestURL := fmt.Sprintf("http://localhost:%d%s", relayerConfig.APIPort, api.RelayAPIPath) // Send request to API { diff --git a/tests/utils/utils.go b/tests/utils/utils.go index fe8493ed..170b16b8 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -204,7 +204,7 @@ func CreateDefaultRelayerConfig( MetricsPort: 9090, SourceBlockchains: sources, DestinationBlockchains: destinations, - APIPort: 1234, + APIPort: 8080, } } From 753bf5be6efdf81faf4ba3f248b084902362d2d3 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 12:43:27 -0400 Subject: [PATCH 40/70] Review fixes --- api/relay_message.go | 31 +++++++++++++++++++++++++--- messages/message_handler.go | 1 + relayer/application_relayer.go | 7 +++---- relayer/message_coordinator.go | 37 +++++++++++++++++++++++++++------- tests/e2e_test.go | 6 ++---- tests/relay_message_api.go | 28 ++++++++++++++++++++++++- types/types.go | 25 ++++------------------- 7 files changed, 95 insertions(+), 40 deletions(-) diff --git a/api/relay_message.go b/api/relay_message.go index 3cc0ba7f..bb1c3271 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -27,6 +27,11 @@ type RelayMessageRequest struct { BlockNum string `json:"block-num"` } +type RelayMessageResponse struct { + // hex encoding of the source blockchain ID for the message + TransactionHash string `json:"transaction-hash"` +} + // Defines a manual warp message to be sent from the relayer through the API. type ManualWarpMessageRequest struct { UnsignedMessageBytes []byte @@ -68,7 +73,17 @@ func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http return } - _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) + resp, err := json.Marshal( + RelayMessageResponse{ + TransactionHash: txHash.Hex(), + }, + ) + if err != nil { + http.Error(w, "error writing response: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write(resp) }) } @@ -94,12 +109,22 @@ func relayAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handle return } - txHash, err := messageCoordinator.ProcessMessage(blockchainID, messageID, blockNum) + txHash, err := messageCoordinator.ProcessMessageID(blockchainID, messageID, blockNum) if err != nil { http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) return } - _, _ = w.Write([]byte("Message processed successfully. Transaction Hash: " + txHash.Hex())) + resp, err := json.Marshal( + RelayMessageResponse{ + TransactionHash: txHash.Hex(), + }, + ) + if err != nil { + http.Error(w, "error writing response: "+err.Error(), http.StatusInternalServerError) + return + } + + _, _ = w.Write(resp) }) } diff --git a/messages/message_handler.go b/messages/message_handler.go index 428a8fd1..3beac8bc 100644 --- a/messages/message_handler.go +++ b/messages/message_handler.go @@ -27,6 +27,7 @@ type MessageHandler interface { // SendMessage sends the signed message to the destination chain. The payload parsed according to // the VM rules is also passed in, since MessageManager does not assume any particular VM + // returns the transaction hash if the transaction is successful. SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) (common.Hash, error) // GetMessageRoutingInfo returns the source chain ID, origin sender address, destination chain ID, and destination address diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 9c6c36d4..d2ba8c08 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -184,6 +184,7 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, handlers []messages.Me } // Relays a message to the destination chain. Does not checkpoint the height. +// returns the transaction hash if the message is successfully relayed. func (r *ApplicationRelayer) ProcessMessage(handler messages.MessageHandler) (common.Hash, error) { // Increment the request ID. Make sure we don't hold the lock while we relay the message. r.lock.Lock() @@ -191,16 +192,14 @@ func (r *ApplicationRelayer) ProcessMessage(handler messages.MessageHandler) (co reqID := r.currentRequestID r.lock.Unlock() - return r.relayMessage( - reqID, - handler, - ) + return r.relayMessage(reqID, handler) } func (r *ApplicationRelayer) RelayerID() database.RelayerID { return r.relayerID } +// returns the transaction hash if the message is successfully relayed. func (r *ApplicationRelayer) relayMessage( requestID uint32, handler messages.MessageHandler, diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 197a250a..6c3f8455 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -4,6 +4,7 @@ package relayer import ( + "context" "errors" "fmt" "math/big" @@ -15,10 +16,15 @@ import ( relayerTypes "github.com/ava-labs/awm-relayer/types" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/ethclient" + "github.com/ava-labs/subnet-evm/interfaces" + "github.com/ava-labs/subnet-evm/precompile/contracts/warp" "github.com/ethereum/go-ethereum/common" "go.uber.org/zap" ) +// MessageCoordinator contains all the logic required to process messages in the relayer. +// Other components such as the listeners or the API should pass messages to the MessageCoordinator +// so that it can parse the message(s) and pass them the the proper ApplicationRelayer. type MessageCoordinator struct { logger logging.Logger // Maps Source blockchain ID and protocol address to a Message Handler Factory @@ -41,11 +47,11 @@ func NewMessageCoordinator( } } -// GetAppRelayerMessageHandler returns the ApplicationRelayer that is configured to handle this message, as well as a +// getAppRelayerMessageHandler returns the ApplicationRelayer that is configured to handle this message, as well as a // one-time MessageHandler instance that the ApplicationRelayer uses to relay this specific message. // The MessageHandler and ApplicationRelayer are decoupled to support batch workflows in which a single ApplicationRelayer // processes multiple messages (using their corresponding MessageHandlers) in a single shot. -func (mc *MessageCoordinator) GetAppRelayerMessageHandler( +func (mc *MessageCoordinator) getAppRelayerMessageHandler( warpMessageInfo *relayerTypes.WarpMessageInfo, ) ( *ApplicationRelayer, @@ -171,7 +177,7 @@ func (mc *MessageCoordinator) ProcessManualWarpMessage( zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), ) - appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) + appRelayer, handler, err := mc.getAppRelayerMessageHandler(warpMessage) if err != nil { mc.logger.Error( "Failed to parse manual Warp message.", @@ -184,20 +190,20 @@ func (mc *MessageCoordinator) ProcessManualWarpMessage( return appRelayer.ProcessMessage(handler) } -func (mc *MessageCoordinator) ProcessMessage(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { +func (mc *MessageCoordinator) ProcessMessageID(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { ethClient, ok := mc.sourceClients[blockchainID] if !ok { mc.logger.Error("Source client not found", zap.String("blockchainID", blockchainID.String())) return common.Hash{}, fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) } - warpMessage, err := relayerTypes.FetchWarpMessageFromID(ethClient, messageID, blockNum) + warpMessage, err := FetchWarpMessageFromID(ethClient, messageID, blockNum) if err != nil { mc.logger.Error("Failed to fetch warp from blockchain", zap.String("blockchainID", blockchainID.String()), zap.Error(err)) return common.Hash{}, fmt.Errorf("could not fetch warp message from ID: %w", err) } - appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpMessage) + appRelayer, handler, err := mc.getAppRelayerMessageHandler(warpMessage) if err != nil { mc.logger.Error( "Failed to parse message", @@ -227,7 +233,7 @@ func (mc *MessageCoordinator) ProcessBlock(blockHeader *types.Header, ethClient // Register each message in the block with the appropriate application relayer messageHandlers := make(map[common.Hash][]messages.MessageHandler) for _, warpLogInfo := range block.Messages { - appRelayer, handler, err := mc.GetAppRelayerMessageHandler(warpLogInfo) + appRelayer, handler, err := mc.getAppRelayerMessageHandler(warpLogInfo) if err != nil { mc.logger.Error( "Failed to parse message", @@ -252,3 +258,20 @@ func (mc *MessageCoordinator) ProcessBlock(blockHeader *types.Header, ethClient go appRelayer.ProcessHeight(block.BlockNumber, handlers, errChan) } } + +func FetchWarpMessageFromID(ethClient ethclient.Client, warpID common.Hash, blockNum *big.Int) (*relayerTypes.WarpMessageInfo, error) { + logs, err := ethClient.FilterLogs(context.Background(), interfaces.FilterQuery{ + Topics: [][]common.Hash{{relayerTypes.WarpPrecompileLogFilter}, nil, {warpID}}, + Addresses: []common.Address{warp.ContractAddress}, + FromBlock: blockNum, + ToBlock: blockNum, + }) + if err != nil { + return nil, fmt.Errorf("could not fetch logs: %w", err) + } + if len(logs) != 1 { + return nil, fmt.Errorf("found more than 1 log: %d", len(logs)) + } + + return relayerTypes.NewWarpMessageInfo(logs[0]) +} diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 98595dcd..872af199 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -21,9 +21,7 @@ const ( warpGenesisFile = "./tests/utils/warp-genesis.json" ) -var ( - localNetworkInstance *local.LocalNetwork -) +var localNetworkInstance *local.LocalNetwork func TestE2E(t *testing.T) { if os.Getenv("RUN_E2E") == "" { @@ -77,7 +75,7 @@ var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { ginkgo.It("Batch Message", func() { BatchRelay(localNetworkInstance) }) - ginkgo.It("Relay Message API", func() { + ginkgo.FIt("Relay Message API", func() { RelayMessageAPI(localNetworkInstance) }) ginkgo.It("Warp API", func() { diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index d71342a9..435c64b2 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -8,9 +8,11 @@ import ( "context" "encoding/json" "fmt" + "io" "net/http" "time" + "github.com/ava-labs/avalanchego/ids" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/awm-relayer/api" testUtils "github.com/ava-labs/awm-relayer/tests/utils" @@ -19,6 +21,7 @@ import ( "github.com/ava-labs/subnet-evm/precompile/contracts/warp" "github.com/ava-labs/teleporter/tests/interfaces" "github.com/ava-labs/teleporter/tests/utils" + teleporterTestUtils "github.com/ava-labs/teleporter/tests/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -40,7 +43,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { testUtils.FundRelayers(ctx, []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, fundedKey, relayerKey) log.Info("Sending teleporter message") - receipt, _, _ := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) + receipt, _, teleporterMessageID := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) warpMessage := getWarpMessageFromLog(ctx, receipt, subnetAInfo) // Set up relayer config @@ -89,6 +92,20 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { res, err := client.Do(req) Expect(err).Should(BeNil()) Expect(res.Status).Should(Equal("200 OK")) + + defer res.Body.Close() + body, err := io.ReadAll(res.Body) + Expect(err).Should(BeNil()) + + var response api.RelayMessageResponse + err = json.Unmarshal(body, &response) + Expect(err).Should(BeNil()) + + receipt, err := subnetBInfo.RPCClient.TransactionReceipt(ctx, common.HexToHash(response.TransactionHash)) + Expect(err).Should(BeNil()) + receiveEvent, err := teleporterTestUtils.GetEventFromLogs(receipt.Logs, subnetBInfo.TeleporterMessenger.ParseReceiveCrossChainMessage) + Expect(err).Should(BeNil()) + Expect(ids.ID(receiveEvent.MessageID)).Should(Equal(teleporterMessageID)) } // Send the same request to ensure the correct response. @@ -104,6 +121,15 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { res, err := client.Do(req) Expect(err).Should(BeNil()) Expect(res.Status).Should(Equal("200 OK")) + + defer res.Body.Close() + body, err := io.ReadAll(res.Body) + Expect(err).Should(BeNil()) + + var response api.RelayMessageResponse + err = json.Unmarshal(body, &response) + Expect(err).Should(BeNil()) + Expect(response.TransactionHash).Should(Equal("0x0000000000000000000000000000000000000000000000000000000000000000")) } // Cancel the command and stop the relayer diff --git a/types/types.go b/types/types.go index 83f53777..b71a6805 100644 --- a/types/types.go +++ b/types/types.go @@ -6,8 +6,6 @@ package types import ( "context" "errors" - "fmt" - "math/big" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/awm-relayer/utils" @@ -18,8 +16,10 @@ import ( "github.com/ethereum/go-ethereum/common" ) -var WarpPrecompileLogFilter = warp.WarpABI.Events["SendWarpMessage"].ID -var ErrInvalidLog = errors.New("invalid warp message log") +var ( + WarpPrecompileLogFilter = warp.WarpABI.Events["SendWarpMessage"].ID + ErrInvalidLog = errors.New("invalid warp message log") +) // WarpBlockInfo describes the block height and logs needed to process Warp messages. // WarpBlockInfo instances are populated by the subscriber, and forwarded to the Listener to process. @@ -108,20 +108,3 @@ func UnpackWarpMessage(unsignedMsgBytes []byte) (*avalancheWarp.UnsignedMessage, } return unsignedMsg, nil } - -func FetchWarpMessageFromID(ethClient ethclient.Client, warpID common.Hash, blockNum *big.Int) (*WarpMessageInfo, error) { - logs, err := ethClient.FilterLogs(context.Background(), interfaces.FilterQuery{ - Topics: [][]common.Hash{{WarpPrecompileLogFilter}, nil, {warpID}}, - Addresses: []common.Address{warp.ContractAddress}, - FromBlock: blockNum, - ToBlock: blockNum, - }) - if err != nil { - return nil, fmt.Errorf("could not fetch logs: %w", err) - } - if len(logs) != 1 { - return nil, fmt.Errorf("found more than 1 log: %d", len(logs)) - } - - return NewWarpMessageInfo(logs[0]) -} From 1d823f5e9fec46aaabcc158b6f065817e2af2439 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 12:51:28 -0400 Subject: [PATCH 41/70] fix --- tests/e2e_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 872af199..be3b9768 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -75,7 +75,7 @@ var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { ginkgo.It("Batch Message", func() { BatchRelay(localNetworkInstance) }) - ginkgo.FIt("Relay Message API", func() { + ginkgo.It("Relay Message API", func() { RelayMessageAPI(localNetworkInstance) }) ginkgo.It("Warp API", func() { From dba6fbe4a763b6718e1f31b0be856a892bf36a60 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 16:59:32 -0400 Subject: [PATCH 42/70] Documentation --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++ api/relay_message.go | 8 +++----- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 64a0380a..73d2682e 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,54 @@ The relayer consists of the following components: +### API + +#### `/relay` +- Used to manually relay a warp message. The body of the request must contain the following JSON: +```json +{ + "blockchain-id": "", + "message-id": "", + "block-num": +} +``` +- If successful, the endpoint will return the following JSON: +```json +{ + "transaction-hash": "" +} +``` + +#### `/relay/message` +- Used to manually relay a warp message. The body of the request must contain the following JSON: +```json +{ + "unsigned-message-bytes": "", + "source-address": "" +} +``` +- If successful, the endpoint will return the following JSON: +```json +{ + "transaction-hash": "", +} +``` + +#### `/health` +- Takes no arguments. Returns a `200` status code if all Application Relayers are healthy. Returns a `503` status if any of the Application Relayers have experienced an unrecoverable error. Here is an example return body: +```json +{ + "status": "down", + "details": { + "relayers-all": { + "status": "down", + "timestamp": "2024-06-01T05:06:07.685522Z", + "error": "" + } + } +} +``` + ## Testing ### Unit Tests diff --git a/api/relay_message.go b/api/relay_message.go index bb1c3271..5ecefdb8 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -34,8 +34,8 @@ type RelayMessageResponse struct { // Defines a manual warp message to be sent from the relayer through the API. type ManualWarpMessageRequest struct { - UnsignedMessageBytes []byte - SourceAddress common.Address + UnsignedMessageBytes []byte `json:"unsigned-message-bytes"` + SourceAddress string `json:"source-address"` } func HandleRelayMessage(messageCoordinator *relayer.MessageCoordinator) { @@ -49,7 +49,6 @@ func HandleRelay(messageCoordinator *relayer.MessageCoordinator) { func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req ManualWarpMessageRequest - err := json.NewDecoder(r.Body).Decode(&req) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) @@ -63,7 +62,7 @@ func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http } warpMessageInfo := &relayerTypes.WarpMessageInfo{ - SourceAddress: req.SourceAddress, + SourceAddress: common.HexToAddress(req.SourceAddress), UnsignedMessage: unsignedMessage, } @@ -90,7 +89,6 @@ func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http func relayAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req RelayMessageRequest - err := json.NewDecoder(r.Body).Decode(&req) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) From 3ae8bf4108ef5ebd7d53cc71cb9df7a2e1e28603 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 17:00:48 -0400 Subject: [PATCH 43/70] Fix test --- tests/manual_message.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/manual_message.go b/tests/manual_message.go index 53452dfc..63a4a214 100644 --- a/tests/manual_message.go +++ b/tests/manual_message.go @@ -103,7 +103,7 @@ func ManualMessage(network interfaces.LocalNetwork) { reqBody := api.ManualWarpMessageRequest{ UnsignedMessageBytes: unsignedMessage.Bytes(), - SourceAddress: offchainregistry.OffChainRegistrySourceAddress, + SourceAddress: offchainregistry.OffChainRegistrySourceAddress.Hex(), } client := http.Client{ From 7694982d2c5fdb42a36b2505143f43b4bee9fe11 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Thu, 27 Jun 2024 17:15:45 -0400 Subject: [PATCH 44/70] Simplify API --- api/relay_message.go | 2 +- relayer/message_coordinator.go | 32 +++++++------------------------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/api/relay_message.go b/api/relay_message.go index 5ecefdb8..ea3d44c6 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -66,7 +66,7 @@ func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http UnsignedMessage: unsignedMessage, } - txHash, err := messageCoordinator.ProcessManualWarpMessage(warpMessageInfo) + txHash, err := messageCoordinator.ProcessWarpMessage(warpMessageInfo) if err != nil { http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) return diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 6c3f8455..f889e9bf 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -168,24 +168,20 @@ func (mc *MessageCoordinator) getApplicationRelayer( return nil } -func (mc *MessageCoordinator) ProcessManualWarpMessage( - warpMessage *relayerTypes.WarpMessageInfo, -) (common.Hash, error) { - // Send any messages that were specified in the configuration - mc.logger.Info( - "Relaying manual Warp message", - zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), - zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), - ) +func (mc *MessageCoordinator) ProcessWarpMessage(warpMessage *relayerTypes.WarpMessageInfo) (common.Hash, error) { appRelayer, handler, err := mc.getAppRelayerMessageHandler(warpMessage) if err != nil { mc.logger.Error( - "Failed to parse manual Warp message.", + "Failed to parse Warp message.", zap.Error(err), zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), ) return common.Hash{}, err } + if appRelayer == nil { + mc.logger.Error("Application relayer not found") + return common.Hash{}, errors.New("application relayer not found") + } return appRelayer.ProcessMessage(handler) } @@ -203,21 +199,7 @@ func (mc *MessageCoordinator) ProcessMessageID(blockchainID ids.ID, messageID co return common.Hash{}, fmt.Errorf("could not fetch warp message from ID: %w", err) } - appRelayer, handler, err := mc.getAppRelayerMessageHandler(warpMessage) - if err != nil { - mc.logger.Error( - "Failed to parse message", - zap.String("blockchainID", warpMessage.UnsignedMessage.SourceChainID.String()), - zap.Error(err), - ) - return common.Hash{}, fmt.Errorf("error getting application relayer: %w", err) - } - if appRelayer == nil { - mc.logger.Error("Application relayer not found") - return common.Hash{}, errors.New("application relayer not found") - } - - return appRelayer.ProcessMessage(handler) + return mc.ProcessWarpMessage(warpMessage) } // Meant to be ran asynchronously. Errors should be sent to errChan. From 570953c69583d01216b35e853915702fe70384df Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:02:50 -0400 Subject: [PATCH 45/70] Update api/relay_message.go Co-authored-by: F. Eugene Aumson Signed-off-by: Geoff Stuart --- api/relay_message.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/relay_message.go b/api/relay_message.go index ea3d44c6..78c3de5e 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -78,7 +78,7 @@ func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http }, ) if err != nil { - http.Error(w, "error writing response: "+err.Error(), http.StatusInternalServerError) + http.Error(w, "error marshaling response: "+err.Error(), http.StatusInternalServerError) return } From 24e7694a5d63e590eaf9aa682af32a8b2c838ee4 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:02:58 -0400 Subject: [PATCH 46/70] Update README.md Co-authored-by: F. Eugene Aumson Signed-off-by: Geoff Stuart --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73d2682e..e451ab65 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,7 @@ The relayer consists of the following components: "relayers-all": { "status": "down", "timestamp": "2024-06-01T05:06:07.685522Z", - "error": "" + "error": "" } } } From abfb84ef333d88926847bc0ad80fb8cf9f5bccf3 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:03:08 -0400 Subject: [PATCH 47/70] Update api/relay_message.go Co-authored-by: F. Eugene Aumson Signed-off-by: Geoff Stuart --- api/relay_message.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/relay_message.go b/api/relay_message.go index 78c3de5e..00afbecc 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -119,7 +119,7 @@ func relayAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handle }, ) if err != nil { - http.Error(w, "error writing response: "+err.Error(), http.StatusInternalServerError) + http.Error(w, "error marshalling response: "+err.Error(), http.StatusInternalServerError) return } From 6995b640092a9501d612f7e10cf410f76c7b810d Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:03:20 -0400 Subject: [PATCH 48/70] Update README.md Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> Signed-off-by: Geoff Stuart --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e451ab65..b524a922 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ The relayer consists of the following components: - If successful, the endpoint will return the following JSON: ```json { - "transaction-hash": "", + "transaction-hash": "", } ``` From 713a80a04aa595c8d7e119a0796ffcb9e87f6cca Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:03:29 -0400 Subject: [PATCH 49/70] Update README.md Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> Signed-off-by: Geoff Stuart --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b524a922..0bfdf84d 100644 --- a/README.md +++ b/README.md @@ -338,7 +338,7 @@ The relayer consists of the following components: - Used to manually relay a warp message. The body of the request must contain the following JSON: ```json { - "unsigned-message-bytes": "", + "unsigned-message-bytes": "", "source-address": "" } ``` From a68f7a43a2af2ab0abd5524e54dd5db83fd28853 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:03:36 -0400 Subject: [PATCH 50/70] Update README.md Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> Signed-off-by: Geoff Stuart --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bfdf84d..a0ddf416 100644 --- a/README.md +++ b/README.md @@ -319,7 +319,7 @@ The relayer consists of the following components: ### API #### `/relay` -- Used to manually relay a warp message. The body of the request must contain the following JSON: +- Used to manually relay a Warp message. The body of the request must contain the following JSON: ```json { "blockchain-id": "", From 4ade9216ace5d4165aac764d8ba8a1691fb20c02 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:03:48 -0400 Subject: [PATCH 51/70] Update README.md Co-authored-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> Signed-off-by: Geoff Stuart --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0ddf416..28844874 100644 --- a/README.md +++ b/README.md @@ -323,7 +323,7 @@ The relayer consists of the following components: ```json { "blockchain-id": "", - "message-id": "", + "message-id": "", "block-num": } ``` From 54682393a7d011f42dc3953254d5568914739445 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:58:06 -0400 Subject: [PATCH 52/70] Review fixes --- README.md | 2 +- api/relay_message.go | 45 +++++++++++++++++++++++++--------- config/config.go | 2 +- main/main.go | 11 ++++----- relayer/message_coordinator.go | 8 +++--- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 28844874..f43f5b62 100644 --- a/README.md +++ b/README.md @@ -324,7 +324,7 @@ The relayer consists of the following components: { "blockchain-id": "", "message-id": "", - "block-num": + "block-num": "" } ``` - If successful, the endpoint will return the following JSON: diff --git a/api/relay_message.go b/api/relay_message.go index 00afbecc..59f7e878 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -5,12 +5,13 @@ import ( "math/big" "net/http" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/awm-relayer/relayer" "github.com/ava-labs/awm-relayer/types" relayerTypes "github.com/ava-labs/awm-relayer/types" - - "github.com/ava-labs/avalanchego/ids" "github.com/ethereum/go-ethereum/common" + "go.uber.org/zap" ) const ( @@ -21,7 +22,7 @@ const ( type RelayMessageRequest struct { // Required. cb58 encoding of the source blockchain ID for the message BlockchainID string `json:"blockchain-id"` - // Required. Hex encoding of the warp message ID + // Required. cb58 encoding of the warp message ID MessageID string `json:"message-id"` // Required. Integer representation of the block number that the message was sent in BlockNum string `json:"block-num"` @@ -38,25 +39,27 @@ type ManualWarpMessageRequest struct { SourceAddress string `json:"source-address"` } -func HandleRelayMessage(messageCoordinator *relayer.MessageCoordinator) { - http.Handle(RelayAPIPath, relayAPIHandler(messageCoordinator)) +func HandleRelayMessage(logger logging.Logger, messageCoordinator *relayer.MessageCoordinator) { + http.Handle(RelayAPIPath, relayAPIHandler(logger, messageCoordinator)) } -func HandleRelay(messageCoordinator *relayer.MessageCoordinator) { - http.Handle(RelayMessageAPIPath, relayMessageAPIHandler(messageCoordinator)) +func HandleRelay(logger logging.Logger, messageCoordinator *relayer.MessageCoordinator) { + http.Handle(RelayMessageAPIPath, relayMessageAPIHandler(logger, messageCoordinator)) } -func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handler { +func relayMessageAPIHandler(logger logging.Logger, messageCoordinator *relayer.MessageCoordinator) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req ManualWarpMessageRequest err := json.NewDecoder(r.Body).Decode(&req) if err != nil { + logger.Warn("could not decode request body") http.Error(w, err.Error(), http.StatusBadRequest) return } unsignedMessage, err := types.UnpackWarpMessage(req.UnsignedMessageBytes) if err != nil { + logger.Warn("error unpacking warp message", zap.Error(err)) http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -68,6 +71,7 @@ func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http txHash, err := messageCoordinator.ProcessWarpMessage(warpMessageInfo) if err != nil { + logger.Error("error processing message", zap.Error(err)) http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) return } @@ -78,37 +82,50 @@ func relayMessageAPIHandler(messageCoordinator *relayer.MessageCoordinator) http }, ) if err != nil { + logger.Error("error marshaling response", zap.Error(err)) http.Error(w, "error marshaling response: "+err.Error(), http.StatusInternalServerError) return } - _, _ = w.Write(resp) + _, err = w.Write(resp) + if err != nil { + logger.Error("error writing response", zap.Error(err)) + } }) } -func relayAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handler { +func relayAPIHandler(logger logging.Logger, messageCoordinator *relayer.MessageCoordinator) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req RelayMessageRequest err := json.NewDecoder(r.Body).Decode(&req) if err != nil { + logger.Warn("could not decode request body") http.Error(w, err.Error(), http.StatusBadRequest) return } blockchainID, err := ids.FromString(req.BlockchainID) if err != nil { + logger.Warn("invalid blockchainID", zap.String("blockchainID", req.BlockchainID)) http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) return } - messageID := common.HexToHash(req.MessageID) + messageID, err := ids.FromString(req.MessageID) + if err != nil { + logger.Warn("invalid messageID", zap.String("messageID", req.MessageID)) + http.Error(w, "invalid messageID: "+err.Error(), http.StatusBadRequest) + return + } blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) if !ok { + logger.Warn("invalid blockNum", zap.String("blockNum", req.BlockNum)) http.Error(w, "invalid blockNum", http.StatusBadRequest) return } txHash, err := messageCoordinator.ProcessMessageID(blockchainID, messageID, blockNum) if err != nil { + logger.Error("error processing message", zap.Error(err)) http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) return } @@ -119,10 +136,14 @@ func relayAPIHandler(messageCoordinator *relayer.MessageCoordinator) http.Handle }, ) if err != nil { + logger.Error("error marshalling response", zap.Error(err)) http.Error(w, "error marshalling response: "+err.Error(), http.StatusInternalServerError) return } - _, _ = w.Write(resp) + _, err = w.Write(resp) + if err != nil { + logger.Error("error writing response", zap.Error(err)) + } }) } diff --git a/config/config.go b/config/config.go index 590c7527..4712feaa 100644 --- a/config/config.go +++ b/config/config.go @@ -70,7 +70,7 @@ func DisplayUsageText() { // Validates the configuration // Does not modify the public fields as derived from the configuration passed to the application, -// but does initialize private fields available through getters +// but does initialize private fields available through getters. func (c *Config) Validate() error { if len(c.SourceBlockchains) == 0 { return errors.New("relayer not configured to relay from any subnets. A list of source subnets must be provided in the configuration file") diff --git a/main/main.go b/main/main.go index 6d3105c4..5b060c22 100644 --- a/main/main.go +++ b/main/main.go @@ -11,10 +11,6 @@ import ( "os" "strings" - "github.com/ava-labs/awm-relayer/messages" - offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" - "github.com/ava-labs/awm-relayer/messages/teleporter" - "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" @@ -23,6 +19,9 @@ import ( "github.com/ava-labs/awm-relayer/api" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/database" + "github.com/ava-labs/awm-relayer/messages" + offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" + "github.com/ava-labs/awm-relayer/messages/teleporter" "github.com/ava-labs/awm-relayer/peers" "github.com/ava-labs/awm-relayer/relayer" "github.com/ava-labs/awm-relayer/utils" @@ -202,8 +201,8 @@ func main() { // Each Listener goroutine will have an atomic bool that it can set to false to indicate an unrecoverable error api.HandleHealthCheck(relayerHealth) - api.HandleRelay(messageCoordinator) - api.HandleRelayMessage(messageCoordinator) + api.HandleRelay(logger, messageCoordinator) + api.HandleRelayMessage(logger, messageCoordinator) // start the health check server go func() { diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index f889e9bf..49b2c37d 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -186,14 +186,14 @@ func (mc *MessageCoordinator) ProcessWarpMessage(warpMessage *relayerTypes.WarpM return appRelayer.ProcessMessage(handler) } -func (mc *MessageCoordinator) ProcessMessageID(blockchainID ids.ID, messageID common.Hash, blockNum *big.Int) (common.Hash, error) { +func (mc *MessageCoordinator) ProcessMessageID(blockchainID ids.ID, messageID ids.ID, blockNum *big.Int) (common.Hash, error) { ethClient, ok := mc.sourceClients[blockchainID] if !ok { mc.logger.Error("Source client not found", zap.String("blockchainID", blockchainID.String())) return common.Hash{}, fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) } - warpMessage, err := FetchWarpMessageFromID(ethClient, messageID, blockNum) + warpMessage, err := FetchWarpMessage(ethClient, messageID, blockNum) if err != nil { mc.logger.Error("Failed to fetch warp from blockchain", zap.String("blockchainID", blockchainID.String()), zap.Error(err)) return common.Hash{}, fmt.Errorf("could not fetch warp message from ID: %w", err) @@ -241,9 +241,9 @@ func (mc *MessageCoordinator) ProcessBlock(blockHeader *types.Header, ethClient } } -func FetchWarpMessageFromID(ethClient ethclient.Client, warpID common.Hash, blockNum *big.Int) (*relayerTypes.WarpMessageInfo, error) { +func FetchWarpMessage(ethClient ethclient.Client, warpID ids.ID, blockNum *big.Int) (*relayerTypes.WarpMessageInfo, error) { logs, err := ethClient.FilterLogs(context.Background(), interfaces.FilterQuery{ - Topics: [][]common.Hash{{relayerTypes.WarpPrecompileLogFilter}, nil, {warpID}}, + Topics: [][]common.Hash{{relayerTypes.WarpPrecompileLogFilter}, nil, {common.Hash(warpID)}}, Addresses: []common.Address{warp.ContractAddress}, FromBlock: blockNum, ToBlock: blockNum, From 349e6c3f58e4f0502773df2c3e929d873416e628 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 11:59:18 -0400 Subject: [PATCH 53/70] Fix test --- tests/relay_message_api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index 435c64b2..c9b7310f 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -69,7 +69,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { reqBody := api.RelayMessageRequest{ BlockchainID: subnetAInfo.BlockchainID.String(), - MessageID: warpMessage.ID().Hex(), + MessageID: warpMessage.ID().String(), BlockNum: receipt.BlockNumber.String(), } From 343651b3e0bcaae52c09ec0274eee8856a226194 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 13:47:26 -0400 Subject: [PATCH 54/70] review fixes --- README.md | 4 ++-- api/health_check.go | 9 ++++++--- api/relay_message.go | 12 +++--------- main/main.go | 2 +- tests/relay_message_api.go | 2 +- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index f43f5b62..14dcb18d 100644 --- a/README.md +++ b/README.md @@ -323,8 +323,8 @@ The relayer consists of the following components: ```json { "blockchain-id": "", - "message-id": "", - "block-num": "" + "message-id": "", + "block-num": "" } ``` - If successful, the endpoint will return the following JSON: diff --git a/api/health_check.go b/api/health_check.go index 10feaf56..2d6755de 100644 --- a/api/health_check.go +++ b/api/health_check.go @@ -7,16 +7,18 @@ import ( "github.com/alexliesenfeld/health" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" "go.uber.org/atomic" + "go.uber.org/zap" ) const HealthAPIPath = "/health" -func HandleHealthCheck(relayerHealth map[ids.ID]*atomic.Bool) { - http.Handle(HealthAPIPath, healthCheckHandler(relayerHealth)) +func HandleHealthCheck(logger logging.Logger, relayerHealth map[ids.ID]*atomic.Bool) { + http.Handle(HealthAPIPath, healthCheckHandler(logger, relayerHealth)) } -func healthCheckHandler(relayerHealth map[ids.ID]*atomic.Bool) http.Handler { +func healthCheckHandler(logger logging.Logger, relayerHealth map[ids.ID]*atomic.Bool) http.Handler { return health.NewHandler(health.NewChecker( health.WithCheck(health.Check{ Name: "relayers-all", @@ -30,6 +32,7 @@ func healthCheckHandler(relayerHealth map[ids.ID]*atomic.Bool) http.Handler { } if len(unhealthyRelayers) > 0 { + logger.Fatal("relayers are unhealthy for blockchains", zap.Strings("blockchains", unhealthyRelayers)) return fmt.Errorf("relayers are unhealthy for blockchains %v", unhealthyRelayers) } return nil diff --git a/api/relay_message.go b/api/relay_message.go index 59f7e878..4633c16e 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -24,8 +24,8 @@ type RelayMessageRequest struct { BlockchainID string `json:"blockchain-id"` // Required. cb58 encoding of the warp message ID MessageID string `json:"message-id"` - // Required. Integer representation of the block number that the message was sent in - BlockNum string `json:"block-num"` + // Required. Block number that the message was sent in + BlockNum uint64 `json:"block-num"` } type RelayMessageResponse struct { @@ -116,14 +116,8 @@ func relayAPIHandler(logger logging.Logger, messageCoordinator *relayer.MessageC http.Error(w, "invalid messageID: "+err.Error(), http.StatusBadRequest) return } - blockNum, ok := new(big.Int).SetString(req.BlockNum, 10) - if !ok { - logger.Warn("invalid blockNum", zap.String("blockNum", req.BlockNum)) - http.Error(w, "invalid blockNum", http.StatusBadRequest) - return - } - txHash, err := messageCoordinator.ProcessMessageID(blockchainID, messageID, blockNum) + txHash, err := messageCoordinator.ProcessMessageID(blockchainID, messageID, new(big.Int).SetUint64(req.BlockNum)) if err != nil { logger.Error("error processing message", zap.Error(err)) http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) diff --git a/main/main.go b/main/main.go index 5b060c22..0ae77aa0 100644 --- a/main/main.go +++ b/main/main.go @@ -200,7 +200,7 @@ func main() { messageCoordinator := relayer.NewMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) // Each Listener goroutine will have an atomic bool that it can set to false to indicate an unrecoverable error - api.HandleHealthCheck(relayerHealth) + api.HandleHealthCheck(logger, relayerHealth) api.HandleRelay(logger, messageCoordinator) api.HandleRelayMessage(logger, messageCoordinator) diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index c9b7310f..bdb5852b 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -70,7 +70,7 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { reqBody := api.RelayMessageRequest{ BlockchainID: subnetAInfo.BlockchainID.String(), MessageID: warpMessage.ID().String(), - BlockNum: receipt.BlockNumber.String(), + BlockNum: receipt.BlockNumber.Uint64(), } client := http.Client{ From b4376e2919c9c66f928cb3ce1acb839748f08928 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 15:40:18 -0400 Subject: [PATCH 55/70] review fixes --- api/relay_message.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/relay_message.go b/api/relay_message.go index 4633c16e..2364d281 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -29,7 +29,7 @@ type RelayMessageRequest struct { } type RelayMessageResponse struct { - // hex encoding of the source blockchain ID for the message + // hex encoding of the transaction hash containing the processed message TransactionHash string `json:"transaction-hash"` } From f33e56f25802224cef4e103162e42b163d5b8e11 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 2 Jul 2024 15:46:02 -0400 Subject: [PATCH 56/70] Capitalize log messages --- api/health_check.go | 2 +- api/relay_message.go | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/api/health_check.go b/api/health_check.go index 2d6755de..1fafe3d8 100644 --- a/api/health_check.go +++ b/api/health_check.go @@ -32,7 +32,7 @@ func healthCheckHandler(logger logging.Logger, relayerHealth map[ids.ID]*atomic. } if len(unhealthyRelayers) > 0 { - logger.Fatal("relayers are unhealthy for blockchains", zap.Strings("blockchains", unhealthyRelayers)) + logger.Fatal("Relayers are unhealthy for blockchains", zap.Strings("blockchains", unhealthyRelayers)) return fmt.Errorf("relayers are unhealthy for blockchains %v", unhealthyRelayers) } return nil diff --git a/api/relay_message.go b/api/relay_message.go index 2364d281..164b2426 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -52,14 +52,14 @@ func relayMessageAPIHandler(logger logging.Logger, messageCoordinator *relayer.M var req ManualWarpMessageRequest err := json.NewDecoder(r.Body).Decode(&req) if err != nil { - logger.Warn("could not decode request body") + logger.Warn("Could not decode request body") http.Error(w, err.Error(), http.StatusBadRequest) return } unsignedMessage, err := types.UnpackWarpMessage(req.UnsignedMessageBytes) if err != nil { - logger.Warn("error unpacking warp message", zap.Error(err)) + logger.Warn("Error unpacking warp message", zap.Error(err)) http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -71,7 +71,7 @@ func relayMessageAPIHandler(logger logging.Logger, messageCoordinator *relayer.M txHash, err := messageCoordinator.ProcessWarpMessage(warpMessageInfo) if err != nil { - logger.Error("error processing message", zap.Error(err)) + logger.Error("Error processing message", zap.Error(err)) http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) return } @@ -82,14 +82,14 @@ func relayMessageAPIHandler(logger logging.Logger, messageCoordinator *relayer.M }, ) if err != nil { - logger.Error("error marshaling response", zap.Error(err)) + logger.Error("Error marshaling response", zap.Error(err)) http.Error(w, "error marshaling response: "+err.Error(), http.StatusInternalServerError) return } _, err = w.Write(resp) if err != nil { - logger.Error("error writing response", zap.Error(err)) + logger.Error("Error writing response", zap.Error(err)) } }) } @@ -99,27 +99,27 @@ func relayAPIHandler(logger logging.Logger, messageCoordinator *relayer.MessageC var req RelayMessageRequest err := json.NewDecoder(r.Body).Decode(&req) if err != nil { - logger.Warn("could not decode request body") + logger.Warn("Could not decode request body") http.Error(w, err.Error(), http.StatusBadRequest) return } blockchainID, err := ids.FromString(req.BlockchainID) if err != nil { - logger.Warn("invalid blockchainID", zap.String("blockchainID", req.BlockchainID)) + logger.Warn("Invalid blockchainID", zap.String("blockchainID", req.BlockchainID)) http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) return } messageID, err := ids.FromString(req.MessageID) if err != nil { - logger.Warn("invalid messageID", zap.String("messageID", req.MessageID)) + logger.Warn("Invalid messageID", zap.String("messageID", req.MessageID)) http.Error(w, "invalid messageID: "+err.Error(), http.StatusBadRequest) return } txHash, err := messageCoordinator.ProcessMessageID(blockchainID, messageID, new(big.Int).SetUint64(req.BlockNum)) if err != nil { - logger.Error("error processing message", zap.Error(err)) + logger.Error("Error processing message", zap.Error(err)) http.Error(w, "error processing message: "+err.Error(), http.StatusInternalServerError) return } @@ -130,14 +130,14 @@ func relayAPIHandler(logger logging.Logger, messageCoordinator *relayer.MessageC }, ) if err != nil { - logger.Error("error marshalling response", zap.Error(err)) + logger.Error("Error marshalling response", zap.Error(err)) http.Error(w, "error marshalling response: "+err.Error(), http.StatusInternalServerError) return } _, err = w.Write(resp) if err != nil { - logger.Error("error writing response", zap.Error(err)) + logger.Error("Error writing response", zap.Error(err)) } }) } From 00d6f4fbc356e336e57c108c34407940502a6286 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 3 Jul 2024 12:32:05 -0400 Subject: [PATCH 57/70] Add lll linter --- .golangci.yml | 1 + config/config.go | 12 ++- config/config_test.go | 33 ++++++-- config/destination_blockchain.go | 15 +++- config/source_blockchain.go | 45 +++++++---- config/viper.go | 16 +++- database/json_file_storage_test.go | 17 +++- database/utils.go | 3 +- main/main.go | 7 +- messages/message_handler.go | 3 +- .../off-chain-registry/message_handler.go | 36 +++++++-- .../message_handler_test.go | 19 ++++- messages/teleporter/message_handler.go | 42 +++++++--- messages/teleporter/message_handler_test.go | 29 +++++-- peers/app_request_network.go | 16 +++- peers/external_handler.go | 20 +++-- .../validators/canonical_validator_client.go | 7 +- relayer/application_relayer.go | 79 +++++++++++++------ relayer/checkpoint/checkpoint.go | 6 +- relayer/listener.go | 4 +- relayer/message_coordinator.go | 57 +++++++++---- tests/allowed_addresses.go | 6 +- tests/basic_relay.go | 23 +++++- tests/batch_relay.go | 8 +- tests/e2e_test.go | 21 +++-- tests/manual_message.go | 21 ++++- tests/relay_message_api.go | 23 +++++- tests/utils/utils.go | 17 +++- utils/client_utils.go | 13 ++- utils/utils.go | 10 ++- utils/utils_test.go | 7 +- vms/destination_client.go | 10 ++- vms/evm/contract_message.go | 4 +- vms/evm/contract_message_test.go | 8 +- vms/evm/destination_client_test.go | 14 +++- vms/evm/signer/kms_signer_test.go | 25 +++--- vms/evm/subscriber.go | 1 + 37 files changed, 510 insertions(+), 168 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 3a7ec484..3a55826f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -23,6 +23,7 @@ linters: - unconvert - unused - whitespace + - lll linters-settings: gofmt: diff --git a/config/config.go b/config/config.go index 4712feaa..b22a01ca 100644 --- a/config/config.go +++ b/config/config.go @@ -52,7 +52,7 @@ type Config struct { RedisURL string `mapstructure:"redis-url" json:"redis-url"` APIPort uint16 `mapstructure:"api-port" json:"api-port"` MetricsPort uint16 `mapstructure:"metrics-port" json:"metrics-port"` - DBWriteIntervalSeconds uint64 `mapstructure:"db-write-interval-seconds" json:"db-write-interval-seconds"` + DBWriteIntervalSeconds uint64 `mapstructure:"db-write-interval-seconds" json:"db-write-interval-seconds"` //nolint:lll 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"` @@ -73,10 +73,10 @@ func DisplayUsageText() { // but does initialize private fields available through getters. func (c *Config) Validate() error { if len(c.SourceBlockchains) == 0 { - return errors.New("relayer not configured to relay from any subnets. A list of source subnets must be provided in the configuration file") + return errors.New("relayer not configured to relay from any subnets. A list of source subnets must be provided in the configuration file") //nolint:lll } 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") + return errors.New("relayer not configured to relay to any subnets. A list of destination subnets must be provided in the configuration file") //nolint:lll } if err := c.PChainAPI.Validate(); err != nil { return err @@ -195,7 +195,11 @@ func (c *Config) InitializeWarpQuorums() error { for _, destinationSubnet := range c.DestinationBlockchains { err := destinationSubnet.initializeWarpQuorum() if err != nil { - return fmt.Errorf("failed to initialize Warp quorum for destination subnet %s: %w", destinationSubnet.SubnetID, err) + return fmt.Errorf( + "failed to initialize Warp quorum for destination subnet %s: %w", + destinationSubnet.SubnetID, + err, + ) } } diff --git a/config/config_test.go b/config/config_test.go index a2221617..cb924bc1 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -78,8 +78,14 @@ func TestGetRelayerAccountPrivateKey_set_pk_in_config(t *testing.T) { resultVerifier: func(c Config) bool { // All destination subnets should have the default private key for i, subnet := range c.DestinationBlockchains { - if subnet.AccountPrivateKey != utils.SanitizeHexString(TestValidConfig.DestinationBlockchains[i].AccountPrivateKey) { - fmt.Printf("expected: %s, got: %s\n", utils.SanitizeHexString(TestValidConfig.DestinationBlockchains[i].AccountPrivateKey), subnet.AccountPrivateKey) + if subnet.AccountPrivateKey != utils.SanitizeHexString( + TestValidConfig.DestinationBlockchains[i].AccountPrivateKey, + ) { + fmt.Printf( + "expected: %s, got: %s\n", + utils.SanitizeHexString(TestValidConfig.DestinationBlockchains[i].AccountPrivateKey), + subnet.AccountPrivateKey, + ) return false } } @@ -102,18 +108,30 @@ func TestGetRelayerAccountPrivateKey_set_pk_with_subnet_env(t *testing.T) { }, envSetter: func() { // Overwrite the PK for the first subnet using an env var - varName := fmt.Sprintf("%s_%s", accountPrivateKeyEnvVarName, TestValidConfig.DestinationBlockchains[0].BlockchainID) + varName := fmt.Sprintf( + "%s_%s", + accountPrivateKeyEnvVarName, + TestValidConfig.DestinationBlockchains[0].BlockchainID, + ) t.Setenv(varName, testPk2) }, expectedOverwritten: true, resultVerifier: func(c Config) bool { // All destination subnets should have testPk1 if c.DestinationBlockchains[0].AccountPrivateKey != utils.SanitizeHexString(testPk2) { - fmt.Printf("expected: %s, got: %s\n", utils.SanitizeHexString(testPk2), c.DestinationBlockchains[0].AccountPrivateKey) + fmt.Printf( + "expected: %s, got: %s\n", + utils.SanitizeHexString(testPk2), + c.DestinationBlockchains[0].AccountPrivateKey, + ) return false } if c.DestinationBlockchains[1].AccountPrivateKey != utils.SanitizeHexString(testPk1) { - fmt.Printf("expected: %s, got: %s\n", utils.SanitizeHexString(testPk1), c.DestinationBlockchains[1].AccountPrivateKey) + fmt.Printf( + "expected: %s, got: %s\n", + utils.SanitizeHexString(testPk1), + c.DestinationBlockchains[1].AccountPrivateKey, + ) return false } return true @@ -339,7 +357,10 @@ func TestGetWarpQuorum(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { client := mock_ethclient.NewMockClient(gomock.NewController(t)) gomock.InOrder( - client.EXPECT().ChainConfig(gomock.Any()).Return(&testCase.chainConfig, nil).Times(testCase.getChainConfigCalls), + client.EXPECT().ChainConfig(gomock.Any()).Return( + &testCase.chainConfig, + nil, + ).Times(testCase.getChainConfigCalls), ) quorum, err := getWarpQuorum(testCase.subnetID, testCase.blockchainID, client) diff --git a/config/destination_blockchain.go b/config/destination_blockchain.go index 80cd9ed0..fb3c2814 100644 --- a/config/destination_blockchain.go +++ b/config/destination_blockchain.go @@ -10,7 +10,8 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) -// Destination blockchain configuration. Specifies how to connect to and issue transactions on the desination blockchain. +// Destination blockchain configuration. Specifies how to connect to and issue +// transactions on the desination blockchain. type DestinationBlockchain struct { SubnetID string `mapstructure:"subnet-id" json:"subnet-id"` BlockchainID string `mapstructure:"blockchain-id" json:"blockchain-id"` @@ -34,7 +35,10 @@ func (s *DestinationBlockchain) Validate() error { return fmt.Errorf("invalid subnetID in destination subnet configuration. Provided ID: %s", s.SubnetID) } if _, err := ids.FromString(s.BlockchainID); err != nil { - return fmt.Errorf("invalid blockchainID in destination subnet configuration. Provided ID: %s", s.BlockchainID) + return fmt.Errorf( + "invalid blockchainID in destination subnet configuration. Provided ID: %s", + s.BlockchainID, + ) } if err := s.RPCEndpoint.Validate(); err != nil { return fmt.Errorf("invalid rpc-endpoint in destination subnet configuration: %w", err) @@ -91,7 +95,12 @@ func (s *DestinationBlockchain) initializeWarpQuorum() error { return fmt.Errorf("invalid subnetID in configuration. error: %w", err) } - client, err := utils.NewEthClientWithConfig(context.Background(), s.RPCEndpoint.BaseURL, s.RPCEndpoint.HTTPHeaders, s.RPCEndpoint.QueryParams) + client, err := utils.NewEthClientWithConfig( + context.Background(), + s.RPCEndpoint.BaseURL, + s.RPCEndpoint.HTTPHeaders, + s.RPCEndpoint.QueryParams, + ) if err != nil { return fmt.Errorf("failed to dial destination blockchain %s: %w", blockchainID, err) } diff --git a/config/source_blockchain.go b/config/source_blockchain.go index d3e6f035..fc39ed50 100644 --- a/config/source_blockchain.go +++ b/config/source_blockchain.go @@ -16,15 +16,15 @@ import ( // Specifies the height from which to start processing historical blocks. type SourceBlockchain struct { SubnetID string `mapstructure:"subnet-id" json:"subnet-id"` - BlockchainID string `mapstructure:"blockchain-id" json:"blockchain-id"` + BlockchainID string `mapstructure:"blockchain-id" json:"blockchain-id"` //nolint:lll VM string `mapstructure:"vm" json:"vm"` - RPCEndpoint APIConfig `mapstructure:"rpc-endpoint" json:"rpc-endpoint"` - WSEndpoint APIConfig `mapstructure:"ws-endpoint" json:"ws-endpoint"` - MessageContracts map[string]MessageProtocolConfig `mapstructure:"message-contracts" json:"message-contracts"` - SupportedDestinations []*SupportedDestination `mapstructure:"supported-destinations" json:"supported-destinations"` - ProcessHistoricalBlocksFromHeight uint64 `mapstructure:"process-historical-blocks-from-height" json:"process-historical-blocks-from-height"` - AllowedOriginSenderAddresses []string `mapstructure:"allowed-origin-sender-addresses" json:"allowed-origin-sender-addresses"` - WarpAPIEndpoint APIConfig `mapstructure:"warp-api-endpoint" json:"warp-api-endpoint"` + RPCEndpoint APIConfig `mapstructure:"rpc-endpoint" json:"rpc-endpoint"` //nolint:lll + WSEndpoint APIConfig `mapstructure:"ws-endpoint" json:"ws-endpoint"` //nolint:lll + MessageContracts map[string]MessageProtocolConfig `mapstructure:"message-contracts" json:"message-contracts"` //nolint:lll + SupportedDestinations []*SupportedDestination `mapstructure:"supported-destinations" json:"supported-destinations"` //nolint:lll + ProcessHistoricalBlocksFromHeight uint64 `mapstructure:"process-historical-blocks-from-height" json:"process-historical-blocks-from-height"` //nolint:lll + AllowedOriginSenderAddresses []string `mapstructure:"allowed-origin-sender-addresses" json:"allowed-origin-sender-addresses"` //nolint:lll + WarpAPIEndpoint APIConfig `mapstructure:"warp-api-endpoint" json:"warp-api-endpoint"` //nolint:lll // convenience fields to access parsed data after initialization subnetID ids.ID @@ -33,9 +33,9 @@ type SourceBlockchain struct { useAppRequestNetwork bool } -// 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 +// 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. func (s *SourceBlockchain) Validate(destinationBlockchainIDs *set.Set[string]) error { if _, err := ids.FromString(s.SubnetID); err != nil { return fmt.Errorf("invalid subnetID in source subnet configuration. Provided ID: %s", s.SubnetID) @@ -104,18 +104,25 @@ func (s *SourceBlockchain) Validate(destinationBlockchainIDs *set.Set[string]) e return fmt.Errorf("invalid blockchainID in configuration. error: %w", err) } if !destinationBlockchainIDs.Contains(dest.BlockchainID) { - return fmt.Errorf("configured source subnet %s has a supported destination blockchain ID %s that is not configured as a destination blockchain", + return fmt.Errorf( + "configured source subnet %s has a supported destination blockchain ID %s that is not configured as a destination blockchain", //nolint:lll s.SubnetID, blockchainID) } dest.blockchainID = blockchainID for _, addressStr := range dest.Addresses { if !common.IsHexAddress(addressStr) { - return fmt.Errorf("invalid allowed destination address in source blockchain configuration: %s", addressStr) + return fmt.Errorf( + "invalid allowed destination address in source blockchain configuration: %s", + addressStr, + ) } address := common.HexToAddress(addressStr) if address == utils.ZeroAddress { - return fmt.Errorf("invalid allowed destination address in source blockchain configuration: %s", addressStr) + return fmt.Errorf( + "invalid allowed destination address in source blockchain configuration: %s", + addressStr, + ) } dest.addresses = append(dest.addresses, address) } @@ -125,11 +132,17 @@ func (s *SourceBlockchain) Validate(destinationBlockchainIDs *set.Set[string]) e allowedOriginSenderAddresses := make([]common.Address, len(s.AllowedOriginSenderAddresses)) for i, addressStr := range s.AllowedOriginSenderAddresses { if !common.IsHexAddress(addressStr) { - return fmt.Errorf("invalid allowed origin sender address in source blockchain configuration: %s", addressStr) + return fmt.Errorf( + "invalid allowed origin sender address in source blockchain configuration: %s", + addressStr, + ) } address := common.HexToAddress(addressStr) if address == utils.ZeroAddress { - return fmt.Errorf("invalid allowed origin sender address in source blockchain configuration: %s", addressStr) + return fmt.Errorf( + "invalid allowed origin sender address in source blockchain configuration: %s", + addressStr, + ) } allowedOriginSenderAddresses[i] = address } diff --git a/config/viper.go b/config/viper.go index 0273ea96..7ea590c4 100644 --- a/config/viper.go +++ b/config/viper.go @@ -89,12 +89,22 @@ func BuildConfig(v *viper.Viper) (Config, error) { privateKey := subnet.AccountPrivateKey if accountPrivateKey != "" { privateKey = accountPrivateKey - cfg.overwrittenOptions = append(cfg.overwrittenOptions, fmt.Sprintf("destination-blockchain(%s).account-private-key", subnet.blockchainID)) + cfg.overwrittenOptions = append( + cfg.overwrittenOptions, + fmt.Sprintf("destination-blockchain(%s).account-private-key", subnet.blockchainID), + ) // Otherwise, check for private keys suffixed with the chain ID and set it for that subnet // Since the key is dynamic, this is only possible through environment variables - } else if privateKeyFromEnv := os.Getenv(fmt.Sprintf("%s_%s", accountPrivateKeyEnvVarName, subnet.BlockchainID)); privateKeyFromEnv != "" { + } else if privateKeyFromEnv := os.Getenv(fmt.Sprintf( + "%s_%s", + accountPrivateKeyEnvVarName, + subnet.BlockchainID, + )); privateKeyFromEnv != "" { privateKey = privateKeyFromEnv - cfg.overwrittenOptions = append(cfg.overwrittenOptions, fmt.Sprintf("destination-blockchain(%s).account-private-key", subnet.blockchainID)) + cfg.overwrittenOptions = append(cfg.overwrittenOptions, fmt.Sprintf( + "destination-blockchain(%s).account-private-key", + subnet.blockchainID), + ) } cfg.DestinationBlockchains[i].AccountPrivateKey = utils.SanitizeHexString(privateKey) } diff --git a/database/json_file_storage_test.go b/database/json_file_storage_test.go index 7e524714..9361a730 100644 --- a/database/json_file_storage_test.go +++ b/database/json_file_storage_test.go @@ -70,10 +70,16 @@ func TestConcurrentWriteReadSingleChain(t *testing.T) { if !success { t.Fatalf("failed to convert latest block to big.Int. err: %v", err) } - assert.Equal(t, finalTargetValue, latestProcessedBlock.Uint64(), "latest processed block height is not correct.") + assert.Equal( + t, + finalTargetValue, + latestProcessedBlock.Uint64(), + "latest processed block height is not correct.", + ) } -// Test that the JSON database can write and read from multiple chains concurrently. Write to any given chain are not concurrent. +// Test that the JSON database can write and read from multiple chains concurrently. +// Writes to any given chain are not concurrent. func TestConcurrentWriteReadMultipleChains(t *testing.T) { relayerIDs := createRelayerIDs( []ids.ID{ @@ -111,7 +117,12 @@ func TestConcurrentWriteReadMultipleChains(t *testing.T) { if !success { t.Fatalf("failed to convert latest block to big.Int. err: %v", err) } - assert.Equal(t, finalTargetValue, latestProcessedBlock.Uint64(), fmt.Sprintf("latest processed block height is not correct. networkID: %d", i)) + assert.Equal( + t, + finalTargetValue, + latestProcessedBlock.Uint64(), + fmt.Sprintf("latest processed block height is not correct. networkID: %d", i), + ) } } diff --git a/database/utils.go b/database/utils.go index 00d1f741..4ea0a563 100644 --- a/database/utils.go +++ b/database/utils.go @@ -18,7 +18,8 @@ func IsKeyNotFoundError(err error) bool { // Determines the height to process from. There are three cases: // 1) The database contains the latest processed block data for the chain -// - In this case, we return the maximum of the latest processed block and the configured processHistoricalBlocksFromHeight +// - In this case, we return the maximum of the latest processed block and the +// configured processHistoricalBlocksFromHeight // // 2) The database has been configured for the chain, but does not contain the latest processed block data // - In this case, we return the configured processHistoricalBlocksFromHeight diff --git a/main/main.go b/main/main.go index 0ae77aa0..a2abe28f 100644 --- a/main/main.go +++ b/main/main.go @@ -197,7 +197,12 @@ func main() { logger.Fatal("Failed to create application relayers", zap.Error(err)) panic(err) } - messageCoordinator := relayer.NewMessageCoordinator(logger, messageHandlerFactories, applicationRelayers, sourceClients) + messageCoordinator := relayer.NewMessageCoordinator( + logger, + messageHandlerFactories, + applicationRelayers, + sourceClients, + ) // Each Listener goroutine will have an atomic bool that it can set to false to indicate an unrecoverable error api.HandleHealthCheck(logger, relayerHealth) diff --git a/messages/message_handler.go b/messages/message_handler.go index 3beac8bc..8a7419bc 100644 --- a/messages/message_handler.go +++ b/messages/message_handler.go @@ -30,7 +30,8 @@ type MessageHandler interface { // returns the transaction hash if the transaction is successful. SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) (common.Hash, error) - // GetMessageRoutingInfo returns the source chain ID, origin sender address, destination chain ID, and destination address + // GetMessageRoutingInfo returns the source chain ID, origin sender address, + // destination chain ID, and destination address. GetMessageRoutingInfo() ( ids.ID, common.Address, diff --git a/messages/off-chain-registry/message_handler.go b/messages/off-chain-registry/message_handler.go index bf6ca351..75f2c3cf 100644 --- a/messages/off-chain-registry/message_handler.go +++ b/messages/off-chain-registry/message_handler.go @@ -81,8 +81,9 @@ func (m *messageHandler) GetUnsignedMessage() *warp.UnsignedMessage { return m.unsignedMessage } -// ShouldSendMessage returns false if any contract is already registered as the specified version in the TeleporterRegistry contract. -// This is because a single contract address can be registered to multiple versions, but each version may only map to a single contract address. +// ShouldSendMessage returns false if any contract is already registered as the specified version +// in the TeleporterRegistry contract. This is because a single contract address can be registered +// to multiple versions, but each version may only map to a single contract address. func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClient) (bool, error) { addressedPayload, err := warpPayload.ParseAddressedCall(m.unsignedMessage.Payload) if err != nil { @@ -92,7 +93,9 @@ func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClie ) return false, err } - entry, destination, err := teleporterregistry.UnpackTeleporterRegistryWarpPayload(addressedPayload.Payload) + entry, destination, err := teleporterregistry.UnpackTeleporterRegistryWarpPayload( + addressedPayload.Payload, + ) if err != nil { m.logger.Error( "Failed unpacking teleporter registry warp payload", @@ -112,7 +115,10 @@ func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClie // Get the correct destination client from the global map client, ok := destinationClient.Client().(ethclient.Client) if !ok { - panic(fmt.Sprintf("Destination client for chain %s is not an Ethereum client", destinationClient.DestinationBlockchainID().String())) + panic(fmt.Sprintf( + "Destination client for chain %s is not an Ethereum client", + destinationClient.DestinationBlockchainID().String()), + ) } // Check if the version is already registered in the TeleporterRegistry contract. @@ -144,24 +150,38 @@ func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClie return false, nil } -func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) (common.Hash, error) { +func (m *messageHandler) SendMessage( + signedMessage *warp.Message, + destinationClient vms.DestinationClient, +) (common.Hash, error) { // Construct the transaction call data to call the TeleporterRegistry contract. // Only one off-chain registry Warp message is sent at a time, so we hardcode the index to 0 in the call. callData, err := teleporterregistry.PackAddProtocolVersion(0) if err != nil { m.logger.Error( "Failed packing receiveCrossChainMessage call data", - zap.String("destinationBlockchainID", destinationClient.DestinationBlockchainID().String()), + zap.String( + "destinationBlockchainID", + destinationClient.DestinationBlockchainID().String(), + ), zap.String("warpMessageID", signedMessage.ID().String()), ) return common.Hash{}, err } - txHash, err := destinationClient.SendTx(signedMessage, m.factory.registryAddress.Hex(), addProtocolVersionGasLimit, callData) + txHash, err := destinationClient.SendTx( + signedMessage, + m.factory.registryAddress.Hex(), + addProtocolVersionGasLimit, + callData, + ) if err != nil { m.logger.Error( "Failed to send tx.", - zap.String("destinationBlockchainID", destinationClient.DestinationBlockchainID().String()), + zap.String( + "destinationBlockchainID", + destinationClient.DestinationBlockchainID().String(), + ), zap.String("warpMessageID", signedMessage.ID().String()), zap.Error(err), ) diff --git a/messages/off-chain-registry/message_handler_test.go b/messages/off-chain-registry/message_handler_test.go index a71b895f..550dff55 100644 --- a/messages/off-chain-registry/message_handler_test.go +++ b/messages/off-chain-registry/message_handler_test.go @@ -152,9 +152,19 @@ func TestShouldSendMessage(t *testing.T) { // construct the signed message var unsignedMessage *warp.UnsignedMessage if test.isMessageInvalid { - unsignedMessage = createInvalidRegistryUnsignedWarpMessage(t, test.entry, teleporterRegistryAddress, test.destinationBlockchainID) + unsignedMessage = createInvalidRegistryUnsignedWarpMessage( + t, + test.entry, + teleporterRegistryAddress, + test.destinationBlockchainID, + ) } else { - unsignedMessage = createRegistryUnsignedWarpMessage(t, test.entry, teleporterRegistryAddress, test.destinationBlockchainID) + unsignedMessage = createRegistryUnsignedWarpMessage( + t, + test.entry, + teleporterRegistryAddress, + test.destinationBlockchainID, + ) } messageHandler, err := factory.NewMessageHandler(unsignedMessage) require.NoError(t, err) @@ -199,7 +209,10 @@ func createInvalidRegistryUnsignedWarpMessage( payloadBytes, err := teleporterregistry.PackTeleporterRegistryWarpPayload(entry, teleporterRegistryAddress) require.NoError(t, err) - invalidAddressedPayload, err := payload.NewAddressedCall(messageProtocolAddress[:], append(payloadBytes, []byte{1, 2, 3, 4}...)) + invalidAddressedPayload, err := payload.NewAddressedCall( + messageProtocolAddress[:], + append(payloadBytes, []byte{1, 2, 3, 4}...), + ) require.NoError(t, err) invalidUnsignedMessage, err := warp.NewUnsignedMessage( diff --git a/messages/teleporter/message_handler.go b/messages/teleporter/message_handler.go index 0e58e044..c0d06573 100644 --- a/messages/teleporter/message_handler.go +++ b/messages/teleporter/message_handler.go @@ -170,9 +170,13 @@ func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClie return true, nil } -// SendMessage extracts the gasLimit and packs the call data to call the receiveCrossChainMessage method of the Teleporter contract, -// and dispatches transaction construction and broadcast to the destination client -func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) (common.Hash, error) { +// SendMessage extracts the gasLimit and packs the call data to call the receiveCrossChainMessage +// method of the Teleporter contract, and dispatches transaction construction and broadcast to the +// destination client. +func (m *messageHandler) SendMessage( + signedMessage *warp.Message, + destinationClient vms.DestinationClient, +) (common.Hash, error) { destinationBlockchainID := destinationClient.DestinationBlockchainID() teleporterMessageID, err := teleporterUtils.CalculateMessageID( m.factory.protocolAddress, @@ -218,7 +222,10 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli return common.Hash{}, err } // Construct the transaction call data to call the receive cross chain message method of the receiver precompile. - callData, err := teleportermessenger.PackReceiveCrossChainMessage(0, common.HexToAddress(m.factory.messageConfig.RewardAddress)) + callData, err := teleportermessenger.PackReceiveCrossChainMessage( + 0, + common.HexToAddress(m.factory.messageConfig.RewardAddress), + ) if err != nil { m.logger.Error( "Failed packing receiveCrossChainMessage call data", @@ -229,7 +236,12 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli return common.Hash{}, err } - txHash, err := destinationClient.SendTx(signedMessage, m.factory.protocolAddress.Hex(), gasLimit, callData) + txHash, err := destinationClient.SendTx( + signedMessage, + m.factory.protocolAddress.Hex(), + gasLimit, + callData, + ) if err != nil { m.logger.Error( "Failed to send tx.", @@ -257,7 +269,12 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationCli return txHash, nil } -func (m *messageHandler) waitForReceipt(signedMessage *warp.Message, destinationClient vms.DestinationClient, txHash common.Hash, teleporterMessageID ids.ID) error { +func (m *messageHandler) waitForReceipt( + signedMessage *warp.Message, + destinationClient vms.DestinationClient, + txHash common.Hash, + teleporterMessageID ids.ID, +) error { destinationBlockchainID := destinationClient.DestinationBlockchainID() callCtx, callCtxCancel := context.WithTimeout(context.Background(), 30*time.Second) defer callCtxCancel() @@ -292,7 +309,9 @@ func (m *messageHandler) waitForReceipt(signedMessage *warp.Message, destination // parseTeleporterMessage returns the Warp message's corresponding Teleporter message from the cache if it exists. // Otherwise parses the Warp message payload. -func (f *factory) parseTeleporterMessage(unsignedMessage *warp.UnsignedMessage) (*teleportermessenger.TeleporterMessage, error) { +func (f *factory) parseTeleporterMessage( + unsignedMessage *warp.UnsignedMessage, +) (*teleportermessenger.TeleporterMessage, error) { addressedPayload, err := warpPayload.ParseAddressedCall(unsignedMessage.Payload) if err != nil { f.logger.Error( @@ -316,10 +335,15 @@ func (f *factory) parseTeleporterMessage(unsignedMessage *warp.UnsignedMessage) // getTeleporterMessenger returns the Teleporter messenger instance for the destination chain. // Panic instead of returning errors because this should never happen, and if it does, we do not // want to log and swallow the error, since operations after this will fail too. -func (f *factory) getTeleporterMessenger(destinationClient vms.DestinationClient) *teleportermessenger.TeleporterMessenger { +func (f *factory) getTeleporterMessenger( + destinationClient vms.DestinationClient, +) *teleportermessenger.TeleporterMessenger { client, ok := destinationClient.Client().(ethclient.Client) if !ok { - panic(fmt.Sprintf("Destination client for chain %s is not an Ethereum client", destinationClient.DestinationBlockchainID().String())) + panic(fmt.Sprintf( + "Destination client for chain %s is not an Ethereum client", + destinationClient.DestinationBlockchainID().String()), + ) } // Get the teleporter messenger contract diff --git a/messages/teleporter/message_handler_test.go b/messages/teleporter/message_handler_test.go index 00557b49..55d95d44 100644 --- a/messages/teleporter/message_handler_test.go +++ b/messages/teleporter/message_handler_test.go @@ -73,14 +73,26 @@ func TestShouldSendMessage(t *testing.T) { validMessageBytes, err := teleportermessenger.PackTeleporterMessage(validTeleporterMessage) require.NoError(t, err) - validAddressedCall, err := warpPayload.NewAddressedCall(messageProtocolAddress.Bytes(), validMessageBytes) + validAddressedCall, err := warpPayload.NewAddressedCall( + messageProtocolAddress.Bytes(), + validMessageBytes, + ) require.NoError(t, err) sourceBlockchainID := ids.Empty - warpUnsignedMessage, err := warp.NewUnsignedMessage(0, sourceBlockchainID, validAddressedCall.Bytes()) + warpUnsignedMessage, err := warp.NewUnsignedMessage( + 0, + sourceBlockchainID, + validAddressedCall.Bytes(), + ) require.NoError(t, err) - messageID, err := teleporterUtils.CalculateMessageID(messageProtocolAddress, sourceBlockchainID, destinationBlockchainID, validTeleporterMessage.MessageNonce) + messageID, err := teleporterUtils.CalculateMessageID( + messageProtocolAddress, + sourceBlockchainID, + destinationBlockchainID, + validTeleporterMessage.MessageNonce, + ) require.NoError(t, err) messageReceivedInput, err := teleportermessenger.PackMessageReceived(messageID) @@ -92,9 +104,16 @@ func TestShouldSendMessage(t *testing.T) { messageDelivered, err := teleportermessenger.PackMessageReceivedOutput(true) require.NoError(t, err) - invalidAddressedCall, err := warpPayload.NewAddressedCall(messageProtocolAddress.Bytes(), validMessageBytes) + invalidAddressedCall, err := warpPayload.NewAddressedCall( + messageProtocolAddress.Bytes(), + validMessageBytes, + ) require.NoError(t, err) - invalidWarpUnsignedMessage, err := warp.NewUnsignedMessage(0, sourceBlockchainID, append(invalidAddressedCall.Bytes(), []byte{1, 2, 3, 4}...)) + invalidWarpUnsignedMessage, err := warp.NewUnsignedMessage( + 0, + sourceBlockchainID, + append(invalidAddressedCall.Bytes(), []byte{1, 2, 3, 4}...), + ) require.NoError(t, err) testCases := []struct { diff --git a/peers/app_request_network.go b/peers/app_request_network.go index 8a915cde..736154a6 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -263,8 +263,12 @@ func (n *AppRequestNetwork) ConnectToCanonicalValidators(subnetID ids.ID) (*Conn // Private helpers -// Connect to the validators of the source blockchain. For each destination blockchain, verify that we have connected to a threshold of stake. -func (n *AppRequestNetwork) connectToNonPrimaryNetworkPeers(cfg *config.Config, sourceBlockchain *config.SourceBlockchain) error { +// Connect to the validators of the source blockchain. For each destination blockchain, +// verify that we have connected to a threshold of stake. +func (n *AppRequestNetwork) connectToNonPrimaryNetworkPeers( + cfg *config.Config, + sourceBlockchain *config.SourceBlockchain, +) error { subnetID := sourceBlockchain.GetSubnetID() connectedValidators, err := n.ConnectToCanonicalValidators(subnetID) if err != nil { @@ -291,8 +295,12 @@ func (n *AppRequestNetwork) connectToNonPrimaryNetworkPeers(cfg *config.Config, return nil } -// Connect to the validators of the destination blockchains. Verify that we have connected to a threshold of stake for each blockchain. -func (n *AppRequestNetwork) connectToPrimaryNetworkPeers(cfg *config.Config, sourceBlockchain *config.SourceBlockchain) error { +// Connect to the validators of the destination blockchains. Verify that we have connected +// to a threshold of stake for each blockchain. +func (n *AppRequestNetwork) connectToPrimaryNetworkPeers( + cfg *config.Config, + sourceBlockchain *config.SourceBlockchain, +) error { for _, destination := range sourceBlockchain.SupportedDestinations { blockchainID := destination.GetBlockchainID() subnetID := cfg.GetSubnetID(blockchainID) diff --git a/peers/external_handler.go b/peers/external_handler.go index 544597a9..7c925e1a 100644 --- a/peers/external_handler.go +++ b/peers/external_handler.go @@ -37,7 +37,8 @@ type expectedResponses struct { expected, received int } -// Create a new RelayerExternalHandler to forward relevant inbound app messages to the respective Teleporter application relayer, as well as handle timeouts. +// Create a new RelayerExternalHandler to forward relevant inbound app messages to the respective +// Teleporter application relayer, as well as handle timeouts. func NewRelayerExternalHandler( logger logging.Logger, registerer prometheus.Registerer, @@ -76,9 +77,10 @@ func NewRelayerExternalHandler( // For each inboundMessage, OnFinishedHandling must be called exactly once. However, since we handle relayer messages // async, we must call OnFinishedHandling manually across all code paths. // -// This diagram illustrates how HandleInbound forwards relevant AppResponses to the corresponding Teleporter application relayer. -// On startup, one Relayer goroutine is created per source subnet, which listens to the subscriber for cross-chain messages -// When a cross-chain message is picked up by a Relayer, HandleInbound routes AppResponses traffic to the appropriate Relayer +// This diagram illustrates how HandleInbound forwards relevant AppResponses to the corresponding +// Teleporter application relayer. On startup, one Relayer goroutine is created per source subnet, +// which listens to the subscriber for cross-chain messages. When a cross-chain message is picked +// up by a Relayer, HandleInbound routes AppResponses traffic to the appropriate Relayer. func (h *RelayerExternalHandler) HandleInbound(_ context.Context, inboundMessage message.InboundMessage) { h.log.Debug( "Handling app response", @@ -109,9 +111,13 @@ func (h *RelayerExternalHandler) Disconnected(nodeID ids.NodeID) { ) } -// RegisterRequestID registers an AppRequest by requestID, and marks the number of expected responses, equivalent to the number of nodes requested. -// requestID should be globally unique for the lifetime of the AppRequest. This is upper bounded by the timeout duration. -func (h *RelayerExternalHandler) RegisterRequestID(requestID uint32, numExpectedResponses int) chan message.InboundMessage { +// RegisterRequestID registers an AppRequest by requestID, and marks the number of +// expected responses, equivalent to the number of nodes requested. requestID should +// be globally unique for the lifetime of the AppRequest. This is upper bounded by the timeout duration. +func (h *RelayerExternalHandler) RegisterRequestID( + requestID uint32, + numExpectedResponses int, +) chan message.InboundMessage { // Create a channel to receive the response h.lock.Lock() defer h.lock.Unlock() diff --git a/peers/validators/canonical_validator_client.go b/peers/validators/canonical_validator_client.go index 225638bc..c3d01dc7 100644 --- a/peers/validators/canonical_validator_client.go +++ b/peers/validators/canonical_validator_client.go @@ -36,7 +36,9 @@ func NewCanonicalValidatorClient(logger logging.Logger, apiConfig *config.APICon } } -func (v *CanonicalValidatorClient) GetCurrentCanonicalValidatorSet(subnetID ids.ID) ([]*avalancheWarp.Validator, uint64, error) { +func (v *CanonicalValidatorClient) GetCurrentCanonicalValidatorSet( + subnetID ids.ID, +) ([]*avalancheWarp.Validator, uint64, error) { height, err := v.GetCurrentHeight(context.Background()) if err != nil { v.logger.Error( @@ -109,7 +111,8 @@ func (v *CanonicalValidatorClient) GetValidatorSet( // as well as their BLS public keys. func (v *CanonicalValidatorClient) getCurrentValidatorSet( ctx context.Context, - subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + subnetID ids.ID, +) (map[ids.NodeID]*validators.GetValidatorOutput, error) { // 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. diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index d2ba8c08..e2bcfde9 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -43,7 +43,8 @@ type blsSignatureBuf [bls.SignatureLen]byte const ( // Number of retries to collect signatures from validators maxRelayerQueryAttempts = 5 - // Maximum amount of time to spend waiting (in addition to network round trip time per attempt) during relayer signature query routine + // Maximum amount of time to spend waiting (in addition to network round trip time per attempt) + // during relayer signature query routine signatureRequestRetryWaitPeriodMs = 10_000 ) @@ -99,7 +100,8 @@ func NewApplicationRelayer( } var signingSubnet ids.ID if sourceBlockchain.GetSubnetID() == constants.PrimaryNetworkID { - // If the message originates from the primary subnet, then we instead "self sign" the message using the validators of the destination subnet. + // If the message originates from the primary subnet, then we instead "self sign" + // the message using the validators of the destination subnet. signingSubnet = cfg.GetSubnetID(relayerID.DestinationBlockchainID) } else { // Otherwise, the source subnet signs the message. @@ -108,12 +110,19 @@ func NewApplicationRelayer( sub := ticker.Subscribe() - checkpointManager := checkpoint.NewCheckpointManager(logger, db, sub, relayerID, startingHeight) + checkpointManager := checkpoint.NewCheckpointManager( + logger, + db, + sub, + relayerID, + startingHeight, + ) checkpointManager.Run() var warpClient *rpc.Client if !sourceBlockchain.UseAppRequestNetwork() { - // The subnet-evm Warp API client does not support query parameters or HTTP headers, and expects the URI to be in a specific form. + // The subnet-evm Warp API client does not support query parameters or HTTP headers + // and expects the URI to be in a specific form. // Instead, we invoke the Warp API directly via the RPC client. warpClient, err = utils.DialWithConfig( context.Background(), @@ -151,12 +160,17 @@ func NewApplicationRelayer( // Process [msgs] at height [height] by relaying each message to the destination chain. // Checkpoints the height with the checkpoint manager when all messages are relayed. -// ProcessHeight is expected to be called for every block greater than or equal to the [startingHeight] provided in the constructor -func (r *ApplicationRelayer) ProcessHeight(height uint64, handlers []messages.MessageHandler, errChan chan error) { +// ProcessHeight is expected to be called for every block greater than or equal to the +// [startingHeight] provided in the constructor. +func (r *ApplicationRelayer) ProcessHeight( + height uint64, + handlers []messages.MessageHandler, + errChan chan error, +) { var eg errgroup.Group for _, handler := range handlers { - // Copy the loop variable to a local variable to avoid the loop variable being captured by the goroutine - // Once we upgrade to Go 1.22, we can use the loop variable directly in the goroutine + // Copy the loop variable to a local variable to avoid the loop variable being captured by the + // goroutine. Once we upgrade to Go 1.22, we can use the loop variable directly in the goroutine. h := handler eg.Go(func() error { _, err := r.ProcessMessage(h) @@ -279,7 +293,9 @@ func (r *ApplicationRelayer) relayMessage( // createSignedMessage fetches the signed Warp message from the source chain via RPC. // Each VM may implement their own RPC method to construct the aggregate signature, which // will need to be accounted for here. -func (r *ApplicationRelayer) createSignedMessage(unsignedMessage *avalancheWarp.UnsignedMessage) (*avalancheWarp.Message, error) { +func (r *ApplicationRelayer) createSignedMessage( + unsignedMessage *avalancheWarp.UnsignedMessage, +) (*avalancheWarp.Message, error) { r.logger.Info("Fetching aggregate signature from the source chain validators via API") var ( @@ -335,8 +351,12 @@ func (r *ApplicationRelayer) createSignedMessage(unsignedMessage *avalancheWarp. return nil, errFailedToGetAggSig } -// createSignedMessageAppRequest collects signatures from nodes by directly querying them via AppRequest, then aggregates the signatures, and constructs the signed warp message. -func (r *ApplicationRelayer) createSignedMessageAppRequest(unsignedMessage *avalancheWarp.UnsignedMessage, requestID uint32) (*avalancheWarp.Message, error) { +// createSignedMessageAppRequest collects signatures from nodes by directly querying them +// via AppRequest, then aggregates the signatures, and constructs the signed warp message. +func (r *ApplicationRelayer) createSignedMessageAppRequest( + unsignedMessage *avalancheWarp.UnsignedMessage, + requestID uint32, +) (*avalancheWarp.Message, error) { r.logger.Info( "Fetching aggregate signature from the source chain validators via AppRequest", zap.String("warpMessageID", unsignedMessage.ID().String()), @@ -390,7 +410,12 @@ func (r *ApplicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval } // Construct the AppRequest - outMsg, err := r.messageCreator.AppRequest(unsignedMessage.SourceChainID, requestID, peers.DefaultAppRequestTimeout, reqBytes) + outMsg, err := r.messageCreator.AppRequest( + unsignedMessage.SourceChainID, + requestID, + peers.DefaultAppRequestTimeout, + reqBytes, + ) if err != nil { r.logger.Error( "Failed to create app request message", @@ -529,8 +554,9 @@ func (r *ApplicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval // Attempts to create a signed warp message from the accumulated responses. // Returns a non-nil Warp message if [accumulatedSignatureWeight] exceeds the signature verification threshold. -// Returns false in the second return parameter if the app response is not relevant to the current signature aggregation request. -// Returns an error only if a non-recoverable error occurs, otherwise returns a nil error to continue processing responses. +// Returns false in the second return parameter if the app response is not relevant to the current signature +// aggregation request. Returns an error only if a non-recoverable error occurs, otherwise returns a nil error +// to continue processing responses. func (r *ApplicationRelayer) handleResponse( response message.InboundMessage, sentTo set.Set[ids.NodeID], @@ -608,10 +634,13 @@ func (r *ApplicationRelayer) handleResponse( return nil, true, err } - signedMsg, err := avalancheWarp.NewMessage(unsignedMessage, &avalancheWarp.BitSetSignature{ - Signers: vdrBitSet.Bytes(), - Signature: *(*[bls.SignatureLen]byte)(bls.SignatureToBytes(aggSig)), - }) + signedMsg, err := avalancheWarp.NewMessage( + unsignedMessage, + &avalancheWarp.BitSetSignature{ + Signers: vdrBitSet.Bytes(), + Signature: *(*[bls.SignatureLen]byte)(bls.SignatureToBytes(aggSig)), + }, + ) if err != nil { r.logger.Error( "Failed to create new signed message", @@ -628,8 +657,9 @@ func (r *ApplicationRelayer) handleResponse( return nil, true, nil } -// isValidSignatureResponse tries to generate a signature from the peer.AsyncResponse, then verifies the signature against the node's public key. -// If we are unable to generate the signature or verify correctly, false will be returned to indicate no valid signature was found in response. +// isValidSignatureResponse tries to generate a signature from the peer.AsyncResponse, then verifies +// the signature against the node's public key. If we are unable to generate the signature or verify +// correctly, false will be returned to indicate no valid signature was found in response. func (r *ApplicationRelayer) isValidSignatureResponse( unsignedMessage *avalancheWarp.UnsignedMessage, response message.InboundMessage, @@ -693,9 +723,12 @@ func (r *ApplicationRelayer) isValidSignatureResponse( return signature, true } -// aggregateSignatures constructs a BLS aggregate signature from the collected validator signatures. Also returns a bit set representing the -// validators that are represented in the aggregate signature. The bit set is in canonical validator order. -func (r *ApplicationRelayer) aggregateSignatures(signatureMap map[int]blsSignatureBuf) (*bls.Signature, set.Bits, error) { +// aggregateSignatures constructs a BLS aggregate signature from the collected validator signatures. Also +// returns a bit set representing the validators that are represented in the aggregate signature. The bit +// set is in canonical validator order. +func (r *ApplicationRelayer) aggregateSignatures( + signatureMap map[int]blsSignatureBuf, +) (*bls.Signature, set.Bits, error) { // Aggregate the signatures signatures := make([]*bls.Signature, 0, len(signatureMap)) vdrBitSet := set.NewBits() diff --git a/relayer/checkpoint/checkpoint.go b/relayer/checkpoint/checkpoint.go index 6d0e0e08..6df59338 100644 --- a/relayer/checkpoint/checkpoint.go +++ b/relayer/checkpoint/checkpoint.go @@ -81,7 +81,11 @@ func (cm *CheckpointManager) writeToDatabase() { zap.Uint64("height", cm.committedHeight), zap.String("relayerID", cm.relayerID.ID.String()), ) - err = cm.database.Put(cm.relayerID.ID, database.LatestProcessedBlockKey, []byte(strconv.FormatUint(cm.committedHeight, 10))) + err = cm.database.Put( + cm.relayerID.ID, + database.LatestProcessedBlockKey, + []byte(strconv.FormatUint(cm.committedHeight, 10)), + ) if err != nil { cm.logger.Error( "Failed to write latest processed block height", diff --git a/relayer/listener.go b/relayer/listener.go index 6a9948fd..bef9fa22 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -138,8 +138,8 @@ func newListener( messageCoordinator: messageCoordinator, } - // Open the subscription. We must do this before processing any missed messages, otherwise we may miss an incoming message - // in between fetching the latest block and subscribing. + // Open the subscription. We must do this before processing any missed messages, otherwise we may + // miss an incoming message in between fetching the latest block and subscribing. err = lstnr.Subscriber.Subscribe(maxSubscribeAttempts) if err != nil { logger.Error( diff --git a/relayer/message_coordinator.go b/relayer/message_coordinator.go index 49b2c37d..f9de1b1c 100644 --- a/relayer/message_coordinator.go +++ b/relayer/message_coordinator.go @@ -47,10 +47,10 @@ func NewMessageCoordinator( } } -// getAppRelayerMessageHandler returns the ApplicationRelayer that is configured to handle this message, as well as a -// one-time MessageHandler instance that the ApplicationRelayer uses to relay this specific message. -// The MessageHandler and ApplicationRelayer are decoupled to support batch workflows in which a single ApplicationRelayer -// processes multiple messages (using their corresponding MessageHandlers) in a single shot. +// getAppRelayerMessageHandler returns the ApplicationRelayer that is configured to handle this message, +// as well as a one-time MessageHandler instance that the ApplicationRelayer uses to relay this specific message. +// The MessageHandler and ApplicationRelayer are decoupled to support batch workflows in which a single +// ApplicationRelayer processes multiple messages (using their corresponding MessageHandlers) in a single shot. func (mc *MessageCoordinator) getAppRelayerMessageHandler( warpMessageInfo *relayerTypes.WarpMessageInfo, ) ( @@ -59,6 +59,7 @@ func (mc *MessageCoordinator) getAppRelayerMessageHandler( error, ) { // Check that the warp message is from a supported message protocol contract address. + //nolint:lll messageHandlerFactory, supportedMessageProtocol := mc.messageHandlerFactories[warpMessageInfo.UnsignedMessage.SourceChainID][warpMessageInfo.SourceAddress] if !supportedMessageProtocol { // Do not return an error here because it is expected for there to be messages from other contracts @@ -76,6 +77,7 @@ func (mc *MessageCoordinator) getAppRelayerMessageHandler( } // Fetch the message delivery data + //nolint:lll sourceBlockchainID, originSenderAddress, destinationBlockchainID, destinationAddress, err := messageHandler.GetMessageRoutingInfo() if err != nil { mc.logger.Error("Failed to get message routing information", zap.Error(err)) @@ -106,9 +108,12 @@ func (mc *MessageCoordinator) getAppRelayerMessageHandler( // Unpacks the Warp message and fetches the appropriate application relayer // Checks for the following registered keys. At most one of these keys should be registered. // 1. An exact match on sourceBlockchainID, destinationBlockchainID, originSenderAddress, and destinationAddress -// 2. A match on sourceBlockchainID and destinationBlockchainID, with a specific originSenderAddress and any destinationAddress -// 3. A match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and a specific destinationAddress -// 4. A match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and any destinationAddress +// 2. A match on sourceBlockchainID and destinationBlockchainID, with a specific originSenderAddress and +// any destinationAddress +// 3. A match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and a +// specific destinationAddress +// 4. A match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and any +// destinationAddress func (mc *MessageCoordinator) getApplicationRelayer( sourceBlockchainID ids.ID, originSenderAddress common.Address, @@ -126,7 +131,8 @@ func (mc *MessageCoordinator) getApplicationRelayer( return applicationRelayer } - // Check for a match on sourceBlockchainID and destinationBlockchainID, with a specific originSenderAddress and any destinationAddress + // Check for a match on sourceBlockchainID and destinationBlockchainID, with a specific + // originSenderAddress and any destinationAddress. applicationRelayerID = database.CalculateRelayerID( sourceBlockchainID, destinationBlockchainID, @@ -137,7 +143,8 @@ func (mc *MessageCoordinator) getApplicationRelayer( return applicationRelayer } - // Check for a match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and a specific destinationAddress + // Check for a match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress + // and a specific destinationAddress. applicationRelayerID = database.CalculateRelayerID( sourceBlockchainID, destinationBlockchainID, @@ -148,7 +155,8 @@ func (mc *MessageCoordinator) getApplicationRelayer( return applicationRelayer } - // Check for a match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress and any destinationAddress + // Check for a match on sourceBlockchainID and destinationBlockchainID, with any originSenderAddress + // and any destinationAddress. applicationRelayerID = database.CalculateRelayerID( sourceBlockchainID, destinationBlockchainID, @@ -186,16 +194,27 @@ func (mc *MessageCoordinator) ProcessWarpMessage(warpMessage *relayerTypes.WarpM return appRelayer.ProcessMessage(handler) } -func (mc *MessageCoordinator) ProcessMessageID(blockchainID ids.ID, messageID ids.ID, blockNum *big.Int) (common.Hash, error) { +func (mc *MessageCoordinator) ProcessMessageID( + blockchainID ids.ID, + messageID ids.ID, + blockNum *big.Int, +) (common.Hash, error) { ethClient, ok := mc.sourceClients[blockchainID] if !ok { - mc.logger.Error("Source client not found", zap.String("blockchainID", blockchainID.String())) + mc.logger.Error( + "Source client not found", + zap.String("blockchainID", blockchainID.String()), + ) return common.Hash{}, fmt.Errorf("source client not set for blockchain: %s", blockchainID.String()) } warpMessage, err := FetchWarpMessage(ethClient, messageID, blockNum) if err != nil { - mc.logger.Error("Failed to fetch warp from blockchain", zap.String("blockchainID", blockchainID.String()), zap.Error(err)) + mc.logger.Error( + "Failed to fetch warp from blockchain", + zap.String("blockchainID", blockchainID.String()), + zap.Error(err), + ) return common.Hash{}, fmt.Errorf("could not fetch warp message from ID: %w", err) } @@ -203,7 +222,11 @@ func (mc *MessageCoordinator) ProcessMessageID(blockchainID ids.ID, messageID id } // Meant to be ran asynchronously. Errors should be sent to errChan. -func (mc *MessageCoordinator) ProcessBlock(blockHeader *types.Header, ethClient ethclient.Client, errChan chan error) { +func (mc *MessageCoordinator) ProcessBlock( + blockHeader *types.Header, + ethClient ethclient.Client, + errChan chan error, +) { // Parse the logs in the block, and group by application relayer block, err := relayerTypes.NewWarpBlockInfo(blockHeader, ethClient) if err != nil { @@ -241,7 +264,11 @@ func (mc *MessageCoordinator) ProcessBlock(blockHeader *types.Header, ethClient } } -func FetchWarpMessage(ethClient ethclient.Client, warpID ids.ID, blockNum *big.Int) (*relayerTypes.WarpMessageInfo, error) { +func FetchWarpMessage( + ethClient ethclient.Client, + warpID ids.ID, + blockNum *big.Int, +) (*relayerTypes.WarpMessageInfo, error) { logs, err := ethClient.FilterLogs(context.Background(), interfaces.FilterQuery{ Topics: [][]common.Hash{{relayerTypes.WarpPrecompileLogFilter}, nil, {common.Hash(warpID)}}, Addresses: []common.Address{warp.ContractAddress}, diff --git a/tests/allowed_addresses.go b/tests/allowed_addresses.go index 4f5f2ca8..dbaa809a 100644 --- a/tests/allowed_addresses.go +++ b/tests/allowed_addresses.go @@ -28,12 +28,14 @@ const relayerCfgFname4 = "relayer-config-4.json" const numKeys = 4 // Tests allowed source and destination address functionality. -// First, relays messages using distinct relayer instances that all write to the same database. The instances are configured to: +// First, relays messages using distinct relayer instances that all write to the same database. +// The instances are configured to: // - Deliver from any source address to any destination address // - Deliver from a specific source address to any destination address // - Deliver from any source address to a specific destination address // - Deliver from a specific source address to a specific destination address -// Then, checks that each relayer instance is able to properly catch up on missed messages that match its particular configuration +// Then, checks that each relayer instance is able to properly catch up on missed messages that +// match its particular configuration. func AllowedAddresses(network interfaces.LocalNetwork) { subnetAInfo := network.GetPrimaryNetworkInfo() subnetBInfo, _ := utils.GetTwoSubnets(network) diff --git a/tests/basic_relay.go b/tests/basic_relay.go index 28bd9735..713aef5c 100644 --- a/tests/basic_relay.go +++ b/tests/basic_relay.go @@ -109,12 +109,26 @@ func BasicRelay(network interfaces.LocalNetwork) { logging.JSON.ConsoleEncoder(), ), ) - jsonDB, err := database.NewJSONFileStorage(logger, relayerConfig.StorageLocation, database.GetConfigRelayerIDs(&relayerConfig)) + jsonDB, err := database.NewJSONFileStorage( + logger, + relayerConfig.StorageLocation, + database.GetConfigRelayerIDs(&relayerConfig), + ) Expect(err).Should(BeNil()) // Create relayer keys that allow all source and destination addresses - relayerIDA := database.CalculateRelayerID(subnetAInfo.BlockchainID, subnetBInfo.BlockchainID, database.AllAllowedAddress, database.AllAllowedAddress) - relayerIDB := database.CalculateRelayerID(subnetBInfo.BlockchainID, subnetAInfo.BlockchainID, database.AllAllowedAddress, database.AllAllowedAddress) + relayerIDA := database.CalculateRelayerID( + subnetAInfo.BlockchainID, + subnetBInfo.BlockchainID, + database.AllAllowedAddress, + database.AllAllowedAddress, + ) + relayerIDB := database.CalculateRelayerID( + subnetBInfo.BlockchainID, + subnetAInfo.BlockchainID, + database.AllAllowedAddress, + database.AllAllowedAddress, + ) // Modify the JSON database to force the relayer to re-process old blocks err = jsonDB.Put(relayerIDA, database.LatestProcessedBlockKey, []byte("0")) Expect(err).Should(BeNil()) @@ -132,7 +146,8 @@ func BasicRelay(network interfaces.LocalNetwork) { relayerCleanup = testUtils.BuildAndRunRelayerExecutable(ctx, relayerConfigPath) defer relayerCleanup() - // We should not receive a new block on subnet B, since the relayer should have seen the Teleporter message was already delivered + // We should not receive a new block on subnet B, since the relayer should have + // seen the Teleporter message was already delivered. log.Info("Waiting for 10s to ensure no new block confirmations on destination chain") Consistently(newHeadsB, 10*time.Second, 500*time.Millisecond).ShouldNot(Receive()) diff --git a/tests/batch_relay.go b/tests/batch_relay.go index 808eb436..aebde3bb 100644 --- a/tests/batch_relay.go +++ b/tests/batch_relay.go @@ -119,7 +119,13 @@ func BatchRelay(network interfaces.LocalNetwork) { } currWait++ if currWait == maxWait { - Expect(false).Should(BeTrue(), fmt.Sprintf("did not receive all sent messages in time. received %d/%d", numMessages-sentMessages.Len(), numMessages)) + Expect(false).Should(BeTrue(), + fmt.Sprintf( + "did not receive all sent messages in time. received %d/%d", + numMessages-sentMessages.Len(), + numMessages, + ), + ) } time.Sleep(1 * time.Second) } diff --git a/tests/e2e_test.go b/tests/e2e_test.go index be3b9768..4ca8cfed 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -36,10 +36,18 @@ func TestE2E(t *testing.T) { var _ = ginkgo.BeforeSuite(func() { localNetworkInstance = local.NewLocalNetwork(warpGenesisFile) // Generate the Teleporter deployment values - teleporterContractAddress := common.HexToAddress(testUtils.ReadHexTextFile("./tests/utils/UniversalTeleporterMessengerContractAddress.txt")) - teleporterDeployerAddress := common.HexToAddress(testUtils.ReadHexTextFile("./tests/utils/UniversalTeleporterDeployerAddress.txt")) - teleporterDeployerTransactionStr := testUtils.ReadHexTextFile("./tests/utils/UniversalTeleporterDeployerTransaction.txt") - teleporterDeployerTransaction, err := hex.DecodeString(utils.SanitizeHexString(teleporterDeployerTransactionStr)) + teleporterContractAddress := common.HexToAddress( + testUtils.ReadHexTextFile("./tests/utils/UniversalTeleporterMessengerContractAddress.txt"), + ) + teleporterDeployerAddress := common.HexToAddress( + testUtils.ReadHexTextFile("./tests/utils/UniversalTeleporterDeployerAddress.txt"), + ) + teleporterDeployerTransactionStr := testUtils.ReadHexTextFile( + "./tests/utils/UniversalTeleporterDeployerTransaction.txt", + ) + teleporterDeployerTransaction, err := hex.DecodeString( + utils.SanitizeHexString(teleporterDeployerTransactionStr), + ) Expect(err).Should(BeNil()) _, fundedKey := localNetworkInstance.GetFundedAccountInfo() @@ -51,7 +59,10 @@ var _ = ginkgo.BeforeSuite(func() { true, ) log.Info("Deployed Teleporter contracts") - localNetworkInstance.DeployTeleporterRegistryContracts(teleporterContractAddress, fundedKey) + localNetworkInstance.DeployTeleporterRegistryContracts( + teleporterContractAddress, + fundedKey, + ) log.Info("Set up ginkgo before suite") }) diff --git a/tests/manual_message.go b/tests/manual_message.go index 63a4a214..400eb5a0 100644 --- a/tests/manual_message.go +++ b/tests/manual_message.go @@ -64,9 +64,24 @@ func ManualMessage(network interfaces.LocalNetwork) { // Set up the nodes to accept the off-chain message // // Create chain config file with off chain message for each chain - unsignedMessage, warpEnabledChainConfigC := teleporterTestUtils.InitOffChainMessageChainConfig(networkID, cChainInfo, newProtocolAddress, 2) - _, warpEnabledChainConfigA := teleporterTestUtils.InitOffChainMessageChainConfig(networkID, subnetAInfo, newProtocolAddress, 2) - _, warpEnabledChainConfigB := teleporterTestUtils.InitOffChainMessageChainConfig(networkID, subnetBInfo, newProtocolAddress, 2) + unsignedMessage, warpEnabledChainConfigC := teleporterTestUtils.InitOffChainMessageChainConfig( + networkID, + cChainInfo, + newProtocolAddress, + 2, + ) + _, warpEnabledChainConfigA := teleporterTestUtils.InitOffChainMessageChainConfig( + networkID, + subnetAInfo, + newProtocolAddress, + 2, + ) + _, warpEnabledChainConfigB := teleporterTestUtils.InitOffChainMessageChainConfig( + networkID, + subnetBInfo, + newProtocolAddress, + 2, + ) // Create chain config with off chain messages chainConfigs := make(map[string]string) diff --git a/tests/relay_message_api.go b/tests/relay_message_api.go index bdb5852b..b737e4c7 100644 --- a/tests/relay_message_api.go +++ b/tests/relay_message_api.go @@ -43,7 +43,13 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { testUtils.FundRelayers(ctx, []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, fundedKey, relayerKey) log.Info("Sending teleporter message") - receipt, _, teleporterMessageID := testUtils.SendBasicTeleporterMessage(ctx, subnetAInfo, subnetBInfo, fundedKey, fundedAddress) + receipt, _, teleporterMessageID := testUtils.SendBasicTeleporterMessage( + ctx, + subnetAInfo, + subnetBInfo, + fundedKey, + fundedAddress, + ) warpMessage := getWarpMessageFromLog(ctx, receipt, subnetAInfo) // Set up relayer config @@ -103,7 +109,10 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { receipt, err := subnetBInfo.RPCClient.TransactionReceipt(ctx, common.HexToHash(response.TransactionHash)) Expect(err).Should(BeNil()) - receiveEvent, err := teleporterTestUtils.GetEventFromLogs(receipt.Logs, subnetBInfo.TeleporterMessenger.ParseReceiveCrossChainMessage) + receiveEvent, err := teleporterTestUtils.GetEventFromLogs( + receipt.Logs, + subnetBInfo.TeleporterMessenger.ParseReceiveCrossChainMessage, + ) Expect(err).Should(BeNil()) Expect(ids.ID(receiveEvent.MessageID)).Should(Equal(teleporterMessageID)) } @@ -129,14 +138,20 @@ func RelayMessageAPI(network interfaces.LocalNetwork) { var response api.RelayMessageResponse err = json.Unmarshal(body, &response) Expect(err).Should(BeNil()) - Expect(response.TransactionHash).Should(Equal("0x0000000000000000000000000000000000000000000000000000000000000000")) + Expect(response.TransactionHash).Should(Equal( + "0x0000000000000000000000000000000000000000000000000000000000000000", + )) } // Cancel the command and stop the relayer relayerCleanup() } -func getWarpMessageFromLog(ctx context.Context, receipt *types.Receipt, source interfaces.SubnetTestInfo) *avalancheWarp.UnsignedMessage { +func getWarpMessageFromLog( + ctx context.Context, + receipt *types.Receipt, + source interfaces.SubnetTestInfo, +) *avalancheWarp.UnsignedMessage { log.Info("Fetching relevant warp logs from the newly produced block") logs, err := source.RPCClient.FilterLogs(ctx, subnetEvmInterfaces.FilterQuery{ BlockHash: &receipt.BlockHash, diff --git a/tests/utils/utils.go b/tests/utils/utils.go index 170b16b8..f8bf27c6 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -297,7 +297,10 @@ func SendBasicTeleporterMessage( input, fundedKey, ) - sendEvent, err := teleporterTestUtils.GetEventFromLogs(receipt.Logs, source.TeleporterMessenger.ParseSendCrossChainMessage) + sendEvent, err := teleporterTestUtils.GetEventFromLogs( + receipt.Logs, + source.TeleporterMessenger.ParseSendCrossChainMessage, + ) Expect(err).Should(BeNil()) return receipt, sendEvent.Message, teleporterMessageID @@ -361,7 +364,10 @@ func RelayBasicMessage( Expect(receipt.Status).Should(Equal(types.ReceiptStatusSuccessful)) // Check that the transaction emits ReceiveCrossChainMessage - receiveEvent, err := teleporterTestUtils.GetEventFromLogs(receipt.Logs, destination.TeleporterMessenger.ParseReceiveCrossChainMessage) + receiveEvent, err := teleporterTestUtils.GetEventFromLogs( + receipt.Logs, + destination.TeleporterMessenger.ParseReceiveCrossChainMessage, + ) Expect(err).Should(BeNil()) Expect(receiveEvent.SourceBlockchainID[:]).Should(Equal(source.BlockchainID[:])) Expect(receiveEvent.MessageID[:]).Should(Equal(teleporterMessageID[:])) @@ -380,7 +386,12 @@ func RelayBasicMessage( receivedTeleporterMessage, err := teleportermessenger.UnpackTeleporterMessage(addressedPayload.Payload) Expect(err).Should(BeNil()) - receivedMessageID, err := teleporterUtils.CalculateMessageID(teleporterContractAddress, source.BlockchainID, destination.BlockchainID, teleporterMessage.MessageNonce) + receivedMessageID, err := teleporterUtils.CalculateMessageID( + teleporterContractAddress, + source.BlockchainID, + destination.BlockchainID, + teleporterMessage.MessageNonce, + ) Expect(err).Should(BeNil()) Expect(receivedMessageID).Should(Equal(teleporterMessageID)) Expect(receivedTeleporterMessage.OriginSenderAddress).Should(Equal(teleporterMessage.OriginSenderAddress)) diff --git a/utils/client_utils.go b/utils/client_utils.go index 051a5705..410fa0b9 100644 --- a/utils/client_utils.go +++ b/utils/client_utils.go @@ -16,7 +16,11 @@ import ( var ErrInvalidEndpoint = errors.New("invalid rpc endpoint") // NewEthClientWithConfig returns an ethclient.Client with the internal RPC client configured with the provided options. -func NewEthClientWithConfig(ctx context.Context, baseURL string, httpHeaders, queryParams map[string]string) (ethclient.Client, error) { +func NewEthClientWithConfig( + ctx context.Context, + baseURL string, httpHeaders, + queryParams map[string]string, +) (ethclient.Client, error) { client, err := DialWithConfig(ctx, baseURL, httpHeaders, queryParams) if err != nil { return nil, err @@ -25,7 +29,12 @@ func NewEthClientWithConfig(ctx context.Context, baseURL string, httpHeaders, qu } // DialWithConfig dials the provided baseURL with the provided httpHeaders and queryParams -func DialWithConfig(ctx context.Context, baseURL string, httpHeaders, queryParams map[string]string) (*rpc.Client, error) { +func DialWithConfig( + ctx context.Context, + baseURL string, + httpHeaders map[string]string, + queryParams map[string]string, +) (*rpc.Client, error) { url, err := addQueryParams(baseURL, queryParams) if err != nil { return nil, err diff --git a/utils/utils.go b/utils/utils.go index 7648c0ef..c96a7588 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -33,8 +33,14 @@ const ( // AWM Utils // -// CheckStakeWeightExceedsThreshold returns true if the accumulated signature weight is at least [quorumNum]/[quorumDen] of [totalWeight]. -func CheckStakeWeightExceedsThreshold(accumulatedSignatureWeight *big.Int, totalWeight uint64, quorumNumerator uint64, quorumDenominator uint64) bool { +// CheckStakeWeightExceedsThreshold returns true if the accumulated signature weight is at +// least [quorumNum]/[quorumDen] of [totalWeight]. +func CheckStakeWeightExceedsThreshold( + accumulatedSignatureWeight *big.Int, + totalWeight uint64, + quorumNumerator uint64, + quorumDenominator uint64, +) bool { if accumulatedSignatureWeight == nil { return false } diff --git a/utils/utils_test.go b/utils/utils_test.go index 2349915b..11854e5a 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -87,7 +87,12 @@ func TestCheckStakeWeightExceedsThreshold(t *testing.T) { } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - actualResult := CheckStakeWeightExceedsThreshold(new(big.Int).SetUint64(testCase.accumulatedSignatureWeight), testCase.totalWeight, testCase.quorumNumerator, testCase.quorumDenominator) + actualResult := CheckStakeWeightExceedsThreshold( + new(big.Int).SetUint64(testCase.accumulatedSignatureWeight), + testCase.totalWeight, + testCase.quorumNumerator, + testCase.quorumDenominator, + ) require.Equal(t, testCase.expectedResult, actualResult) }) } diff --git a/vms/destination_client.go b/vms/destination_client.go index 12e2c895..e1fbda02 100644 --- a/vms/destination_client.go +++ b/vms/destination_client.go @@ -17,8 +17,9 @@ import ( "go.uber.org/zap" ) -// DestinationClient is the interface for the destination chain client. Methods that interact with the destination chain -// should generally be implemented in a thread safe way, as they will be called concurrently by the application relayers. +// DestinationClient is the interface for the destination chain client. Methods that interact with +// the destination chain should generally be implemented in a thread safe way, as they will be called +// concurrently by the application relayers. type DestinationClient interface { // SendTx constructs the transaction from warp primitives, and sends to the configured destination chain endpoint. // Returns the hash of the sent transaction. @@ -45,7 +46,10 @@ func NewDestinationClient(logger logging.Logger, subnetInfo *config.DestinationB } // CreateDestinationClients creates destination clients for all subnets configured as destinations -func CreateDestinationClients(logger logging.Logger, relayerConfig config.Config) (map[ids.ID]DestinationClient, error) { +func CreateDestinationClients( + logger logging.Logger, + relayerConfig config.Config, +) (map[ids.ID]DestinationClient, error) { destinationClients := make(map[ids.ID]DestinationClient) for _, subnetInfo := range relayerConfig.DestinationBlockchains { blockchainID, err := ids.FromString(subnetInfo.BlockchainID) diff --git a/vms/evm/contract_message.go b/vms/evm/contract_message.go index 49ace169..5221d8a3 100644 --- a/vms/evm/contract_message.go +++ b/vms/evm/contract_message.go @@ -24,8 +24,8 @@ func NewContractMessage(logger logging.Logger, subnetInfo config.SourceBlockchai } func (m *contractMessage) UnpackWarpMessage(unsignedMsgBytes []byte) (*avalancheWarp.UnsignedMessage, error) { - // This function may be called with raw UnsignedMessage bytes or with ABI encoded bytes as emitted by the Warp precompile - // The latter case is the steady state behavior, so check that first. The former only occurs on startup. + // This function may be called with raw UnsignedMessage bytes or with ABI encoded bytes as emitted by the Warp + // precompile. The latter case is the steady state behavior, so check that first. The former only occurs on startup. unsignedMsg, err := warp.UnpackSendWarpEventDataToMessage(unsignedMsgBytes) if err != nil { m.logger.Debug( diff --git a/vms/evm/contract_message_test.go b/vms/evm/contract_message_test.go index cff0ec97..150f0961 100644 --- a/vms/evm/contract_message_test.go +++ b/vms/evm/contract_message_test.go @@ -61,24 +61,24 @@ func TestUnpack(t *testing.T) { }{ { name: "valid log data", - input: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000024c00000000053968786a235cbcfb6e57321b94378e95939b773a9626acf7a8cc440075c02c7268000002220000000000010000001452718d4ea91a6dd9a68940dbd687efa32315d11600000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcb1d32d469938520383696931c26b9753662db74ad33c012f41e337aa828f1b74000000000000000000000000abcedf1234abcedf1234abcedf1234abcedf12340000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a100ff48a37cab9f87c8b5da933da46ea1a5fb80000000000000000000000000000000000000000000000000000000000000002acafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + input: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000024c00000000053968786a235cbcfb6e57321b94378e95939b773a9626acf7a8cc440075c02c7268000002220000000000010000001452718d4ea91a6dd9a68940dbd687efa32315d11600000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcb1d32d469938520383696931c26b9753662db74ad33c012f41e337aa828f1b74000000000000000000000000abcedf1234abcedf1234abcedf1234abcedf12340000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a100ff48a37cab9f87c8b5da933da46ea1a5fb80000000000000000000000000000000000000000000000000000000000000002acafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe000000000000000000000000000000000000000000000000000000000000000000000000000000000000", //nolint:lll networkID: constants.DefaultNetworkID, expectError: false, }, { name: "invalid log data", - input: "1000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000024c00000000053968786a235cbcfb6e57321b94378e95939b773a9626acf7a8cc440075c02c7268000002220000000000010000001452718d4ea91a6dd9a68940dbd687efa32315d11600000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcb1d32d469938520383696931c26b9753662db74ad33c012f41e337aa828f1b74000000000000000000000000abcedf1234abcedf1234abcedf1234abcedf12340000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a100ff48a37cab9f87c8b5da933da46ea1a5fb80000000000000000000000000000000000000000000000000000000000000002acafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + input: "1000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000024c00000000053968786a235cbcfb6e57321b94378e95939b773a9626acf7a8cc440075c02c7268000002220000000000010000001452718d4ea91a6dd9a68940dbd687efa32315d11600000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcb1d32d469938520383696931c26b9753662db74ad33c012f41e337aa828f1b74000000000000000000000000abcedf1234abcedf1234abcedf1234abcedf12340000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a100ff48a37cab9f87c8b5da933da46ea1a5fb80000000000000000000000000000000000000000000000000000000000000002acafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe000000000000000000000000000000000000000000000000000000000000000000000000000000000000", //nolint:lll expectError: true, }, { name: "valid standalone message", - input: "00000000053968786a235cbcfb6e57321b94378e95939b773a9626acf7a8cc440075c02c7268000002220000000000010000001452718d4ea91a6dd9a68940dbd687efa32315d11600000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcb1d32d469938520383696931c26b9753662db74ad33c012f41e337aa828f1b74000000000000000000000000abcedf1234abcedf1234abcedf1234abcedf12340000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a100ff48a37cab9f87c8b5da933da46ea1a5fb80000000000000000000000000000000000000000000000000000000000000002acafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe00000000000000000000000000000000000000000000", + input: "00000000053968786a235cbcfb6e57321b94378e95939b773a9626acf7a8cc440075c02c7268000002220000000000010000001452718d4ea91a6dd9a68940dbd687efa32315d11600000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcb1d32d469938520383696931c26b9753662db74ad33c012f41e337aa828f1b74000000000000000000000000abcedf1234abcedf1234abcedf1234abcedf12340000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a100ff48a37cab9f87c8b5da933da46ea1a5fb80000000000000000000000000000000000000000000000000000000000000002acafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe00000000000000000000000000000000000000000000", //nolint:lll networkID: constants.DefaultNetworkID, expectError: false, }, { name: "invalid standalone message", - input: "ab000000053968786a235cbcfb6e57321b94378e95939b773a9626acf7a8cc440075c02c7268000002220000000000010000001452718d4ea91a6dd9a68940dbd687efa32315d11600000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcb1d32d469938520383696931c26b9753662db74ad33c012f41e337aa828f1b74000000000000000000000000abcedf1234abcedf1234abcedf1234abcedf12340000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a100ff48a37cab9f87c8b5da933da46ea1a5fb80000000000000000000000000000000000000000000000000000000000000002acafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe00000000000000000000000000000000000000000000", + input: "ab000000053968786a235cbcfb6e57321b94378e95939b773a9626acf7a8cc440075c02c7268000002220000000000010000001452718d4ea91a6dd9a68940dbd687efa32315d11600000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008db97c7cece249c2b98bdc0226cc4c2a57bf52fcb1d32d469938520383696931c26b9753662db74ad33c012f41e337aa828f1b74000000000000000000000000abcedf1234abcedf1234abcedf1234abcedf12340000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a100ff48a37cab9f87c8b5da933da46ea1a5fb80000000000000000000000000000000000000000000000000000000000000002acafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabecafe00000000000000000000000000000000000000000000", //nolint:lll expectError: true, }, } diff --git a/vms/evm/destination_client_test.go b/vms/evm/destination_client_test.go index 2658545f..b23576b9 100644 --- a/vms/evm/destination_client_test.go +++ b/vms/evm/destination_client_test.go @@ -91,9 +91,17 @@ func TestSendTx(t *testing.T) { toAddress := "0x27aE10273D17Cd7e80de8580A51f476960626e5f" gomock.InOrder( - mockClient.EXPECT().EstimateBaseFee(gomock.Any()).Return(new(big.Int), test.estimateBaseFeeErr).Times(test.estimateBaseFeeTimes), - mockClient.EXPECT().SuggestGasTipCap(gomock.Any()).Return(new(big.Int), test.suggestGasTipCapErr).Times(test.suggestGasTipCapTimes), - mockClient.EXPECT().SendTransaction(gomock.Any(), gomock.Any()).Return(test.sendTransactionErr).Times(test.sendTransactionTimes), + mockClient.EXPECT().EstimateBaseFee(gomock.Any()).Return( + new(big.Int), + test.estimateBaseFeeErr, + ).Times(test.estimateBaseFeeTimes), + mockClient.EXPECT().SuggestGasTipCap(gomock.Any()).Return( + new(big.Int), + test.suggestGasTipCapErr, + ).Times(test.suggestGasTipCapTimes), + mockClient.EXPECT().SendTransaction(gomock.Any(), gomock.Any()).Return( + test.sendTransactionErr, + ).Times(test.sendTransactionTimes), ) _, err := destinationClient.SendTx(warpMsg, toAddress, 0, []byte{}) diff --git a/vms/evm/signer/kms_signer_test.go b/vms/evm/signer/kms_signer_test.go index 22ccfbf7..c67f1385 100644 --- a/vms/evm/signer/kms_signer_test.go +++ b/vms/evm/signer/kms_signer_test.go @@ -22,8 +22,8 @@ func TestRecoverEIP155Signature(t *testing.T) { txHash: "69963cf4839e149fea0e7b0969dd6834ea4b22fa7f9209a46683982320e5edfd", rValue: "00a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee4", sValue: "5964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd079", - pubKey: "04a3b664aa1f37bf6c46a3f2cdb209091e16070208b30244838e2cb9fe1465c4911ba2ab0c54bfc39972740ef7b24904f8740b0a69aca2bbfce0f2829429b9a5c5", - expectedSignature: "a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee45964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd07901", + pubKey: "04a3b664aa1f37bf6c46a3f2cdb209091e16070208b30244838e2cb9fe1465c4911ba2ab0c54bfc39972740ef7b24904f8740b0a69aca2bbfce0f2829429b9a5c5", //nolint:lll + expectedSignature: "a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee45964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd07901", //nolint:lll expectedError: false, }, { @@ -31,8 +31,8 @@ func TestRecoverEIP155Signature(t *testing.T) { txHash: "69963cf4839e149fea0e7b0969dd6834ea4b22fa7f9209a46683982320e5edfd", rValue: "10a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee4", sValue: "5964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd079", - pubKey: "04a3b664aa1f37bf6c46a3f2cdb209091e16070208b30244838e2cb9fe1465c4911ba2ab0c54bfc39972740ef7b24904f8740b0a69aca2bbfce0f2829429b9a5c5", - expectedSignature: "a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee45964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd07901", + pubKey: "04a3b664aa1f37bf6c46a3f2cdb209091e16070208b30244838e2cb9fe1465c4911ba2ab0c54bfc39972740ef7b24904f8740b0a69aca2bbfce0f2829429b9a5c5", //nolint:lll + expectedSignature: "a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee45964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd07901", //nolint:lll expectedError: true, }, { @@ -40,17 +40,18 @@ func TestRecoverEIP155Signature(t *testing.T) { txHash: "69963cf4839e149fea0e7b0969dd6834ea4b22fa7f9209a46683982320e5edfd", rValue: "00a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee4", sValue: "1964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd079", - pubKey: "04a3b664aa1f37bf6c46a3f2cdb209091e16070208b30244838e2cb9fe1465c4911ba2ab0c54bfc39972740ef7b24904f8740b0a69aca2bbfce0f2829429b9a5c5", - expectedSignature: "a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee45964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd07901", + pubKey: "04a3b664aa1f37bf6c46a3f2cdb209091e16070208b30244838e2cb9fe1465c4911ba2ab0c54bfc39972740ef7b24904f8740b0a69aca2bbfce0f2829429b9a5c5", //nolint:lll + expectedSignature: "a94bfca53b42454acc43e7328ce7a8f244a629026095c36c0ee82607377cbee45964b0dd9bf23723570b6a073cb945fd1ba853ebf5579c8d11bb4fdb305cd07901", //nolint:lll expectedError: true, }, { - name: "s-value too high", - txHash: "e29dc6b15950a5433e239155a2208156ba8fd81eeb81ade45329d4b2fb2e8421", - rValue: "00d993636ed097bf04b7c982e0394d572ead3c8f23ecc8baba0bbdfaa91aba4376", - sValue: "00d1ac23b517ae2569522626096fa1922681c87612cceac971e855d42103fea7c6", // This is > secp256k1n/2 - pubKey: "04a3b664aa1f37bf6c46a3f2cdb209091e16070208b30244838e2cb9fe1465c4911ba2ab0c54bfc39972740ef7b24904f8740b0a69aca2bbfce0f2829429b9a5c5", - expectedSignature: "d993636ed097bf04b7c982e0394d572ead3c8f23ecc8baba0bbdfaa91aba43762e53dc4ae851da96add9d9f6905e6dd838e666d3e25dd6c9d77c8a6bcc37997b00", + name: "s-value too high", + txHash: "e29dc6b15950a5433e239155a2208156ba8fd81eeb81ade45329d4b2fb2e8421", + rValue: "00d993636ed097bf04b7c982e0394d572ead3c8f23ecc8baba0bbdfaa91aba4376", + // This is > secp256k1n/2 + sValue: "00d1ac23b517ae2569522626096fa1922681c87612cceac971e855d42103fea7c6", + pubKey: "04a3b664aa1f37bf6c46a3f2cdb209091e16070208b30244838e2cb9fe1465c4911ba2ab0c54bfc39972740ef7b24904f8740b0a69aca2bbfce0f2829429b9a5c5", //nolint:lll + expectedSignature: "d993636ed097bf04b7c982e0394d572ead3c8f23ecc8baba0bbdfaa91aba43762e53dc4ae851da96add9d9f6905e6dd838e666d3e25dd6c9d77c8a6bcc37997b00", //nolint:lll expectedError: false, }, } diff --git a/vms/evm/subscriber.go b/vms/evm/subscriber.go index 95878ebd..8027baeb 100644 --- a/vms/evm/subscriber.go +++ b/vms/evm/subscriber.go @@ -76,6 +76,7 @@ func (s *subscriber) ProcessFromHeight(height *big.Int, done chan bool) { bigLatestBlockHeight := big.NewInt(0).SetUint64(latestBlockHeight) + //nolint:lll for fromBlock := big.NewInt(0).Set(height); fromBlock.Cmp(bigLatestBlockHeight) <= 0; fromBlock.Add(fromBlock, big.NewInt(MaxBlocksPerRequest)) { toBlock := big.NewInt(0).Add(fromBlock, big.NewInt(MaxBlocksPerRequest-1)) From 0f7cc0dfd0ef92c975c003b9fade4134b93e3a53 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Wed, 3 Jul 2024 13:53:35 -0400 Subject: [PATCH 58/70] Update database/utils.go Co-authored-by: Ian Suvak Signed-off-by: Geoff Stuart --- database/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/utils.go b/database/utils.go index 4ea0a563..b0a513a4 100644 --- a/database/utils.go +++ b/database/utils.go @@ -19,7 +19,7 @@ func IsKeyNotFoundError(err error) bool { // Determines the height to process from. There are three cases: // 1) The database contains the latest processed block data for the chain // - In this case, we return the maximum of the latest processed block and the -// configured processHistoricalBlocksFromHeight +// configured processHistoricalBlocksFromHeight. // // 2) The database has been configured for the chain, but does not contain the latest processed block data // - In this case, we return the configured processHistoricalBlocksFromHeight From bcdcadd9c0439cf26e100997d5699adc1a05a90a Mon Sep 17 00:00:00 2001 From: Ian Suvak Date: Wed, 3 Jul 2024 16:31:28 -0400 Subject: [PATCH 59/70] Update CODEOWNERS to use @ava-labs/interop team --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d76929a9..e3bc4d3f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax -* @michaelkaplan13 @cam-schultz @minghinmatthewlam @gwen917 @geoff-vball @bernard-avalabs +* @ava-labs/interop \ No newline at end of file From 07b2c932bb8d861dbd53ac8fc23e892c82c85bd9 Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Fri, 5 Jul 2024 10:13:26 -0400 Subject: [PATCH 60/70] Golang v1.21.12 --- CONTRIBUTING.md | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eeed2395..37357164 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Setup -To start developing on AWM Relayer, you'll need Golang >= v1.21.7. +To start developing on AWM Relayer, you'll need Golang v1.21.12. ## Issues diff --git a/go.mod b/go.mod index 79d7de99..baf45247 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ava-labs/awm-relayer -go 1.21.11 +go 1.21.12 require ( github.com/ava-labs/avalanche-network-runner v1.7.6 From c7075f458d6b516bbc6a8f990edf030384500945 Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Fri, 5 Jul 2024 10:25:21 -0400 Subject: [PATCH 61/70] actions/setup-go uses go.mod --- .github/workflows/e2e.yml | 3 +-- .github/workflows/linter.yml | 3 +-- .github/workflows/release.yml | 3 +-- .github/workflows/test.yml | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 229d674a..018c0d05 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -25,13 +25,12 @@ jobs: - name: Set Go version run: | source ./scripts/versions.sh - echo GO_VERSION=$GO_VERSION >> $GITHUB_ENV echo SUBNET_EVM_VERSION=$SUBNET_EVM_VERSION >> $GITHUB_ENV - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Checkout subnet-evm repository uses: actions/checkout@v4 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 512d5df6..5adb2439 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -25,12 +25,11 @@ jobs: - name: Set Go version run: | source ./scripts/versions.sh - echo GO_VERSION=$GO_VERSION >> $GITHUB_ENV - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Run Lint run: ./scripts/lint.sh --go-lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6272fe92..69398e42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,12 +22,11 @@ jobs: - name: Set Go version run: | source ./awm-relayer/scripts/versions.sh - echo GO_VERSION=$GO_VERSION >> $GITHUB_ENV - name: Set up Go uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Set up arm64 cross compiler run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46b7ead2..8503d3a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,12 +25,11 @@ jobs: - name: Set Go version run: | source ./scripts/versions.sh - echo GO_VERSION=$GO_VERSION >> $GITHUB_ENV - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Run Relayer Unit Tests run: ./scripts/test.sh From 872f41c19795f6052122722d089ed5287c6f6c5c Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Fri, 5 Jul 2024 10:59:03 -0400 Subject: [PATCH 62/70] Clean up workflow setup --- .github/workflows/e2e.yml | 14 -------------- .github/workflows/linter.yml | 4 ---- .github/workflows/release.yml | 4 ---- .github/workflows/test.yml | 4 ---- 4 files changed, 26 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 018c0d05..6820c047 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -32,22 +32,8 @@ jobs: with: go-version-file: 'go.mod' - - name: Checkout subnet-evm repository - uses: actions/checkout@v4 - with: - repository: ava-labs/subnet-evm - ref: ${{ env.SUBNET_EVM_VERSION }} - - name: Install AvalancheGo Release run: BASEDIR=/tmp/e2e-test AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego ./scripts/install_avalanchego_release.sh - - name: Build Subnet-EVM Plugin Binary - run: ./scripts/build.sh /tmp/e2e-test/avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy - - - name: Checkout awm-relayer repository - uses: actions/checkout@v4 - with: - submodules: recursive - - 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 5adb2439..67cc879f 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -22,10 +22,6 @@ jobs: with: submodules: recursive - - name: Set Go version - run: | - source ./scripts/versions.sh - - name: Setup Go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 69398e42..6355a688 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,10 +19,6 @@ jobs: path: awm-relayer submodules: recursive - - name: Set Go version - run: | - source ./awm-relayer/scripts/versions.sh - - name: Set up Go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8503d3a5..a7de23a1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,10 +21,6 @@ jobs: uses: actions/checkout@v4 with: submodules: recursive - - - name: Set Go version - run: | - source ./scripts/versions.sh - name: Setup Go uses: actions/setup-go@v5 From 5115e9f80cb66ee442c1c38e5180562a144f1d38 Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Fri, 5 Jul 2024 11:18:38 -0400 Subject: [PATCH 63/70] Still set GO_VERSION where needed --- .github/workflows/e2e.yml | 24 +++++++++++++++++++----- .github/workflows/release.yml | 6 ++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 6820c047..5b77e647 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -21,19 +21,33 @@ jobs: uses: actions/checkout@v4 with: submodules: recursive - - - name: Set Go version - run: | - source ./scripts/versions.sh - echo SUBNET_EVM_VERSION=$SUBNET_EVM_VERSION >> $GITHUB_ENV - name: Setup Go uses: actions/setup-go@v5 with: go-version-file: 'go.mod' + - name: Set subnet-evm version + run: | + source ./scripts/versions.sh + echo SUBNET_EVM_VERSION=$SUBNET_EVM_VERSION >> $GITHUB_ENV + + - name: Checkout subnet-evm repository + uses: actions/checkout@v4 + with: + repository: ava-labs/subnet-evm + ref: ${{ env.SUBNET_EVM_VERSION }} + - name: Install AvalancheGo Release run: BASEDIR=/tmp/e2e-test AVALANCHEGO_BUILD_PATH=/tmp/e2e-test/avalanchego ./scripts/install_avalanchego_release.sh + - name: Build Subnet-EVM Plugin Binary + run: ./scripts/build.sh /tmp/e2e-test/avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy + + - name: Checkout awm-relayer repository + uses: actions/checkout@v4 + with: + submodules: recursive + - 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/release.yml b/.github/workflows/release.yml index 6355a688..9887416e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,12 @@ jobs: path: awm-relayer submodules: recursive + # The GO_VERSION must be set explicitly to be used by GoReleaser and in the Dockerfile. + - name: Set Go version + run: | + source ./awm-relayer/scripts/versions.sh + echo GO_VERSION=$GO_VERSION >> $GITHUB_ENV + - name: Set up Go uses: actions/setup-go@v5 with: From f81deabd62909d96bae25066fc95da72dd007214 Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Fri, 5 Jul 2024 11:23:42 -0400 Subject: [PATCH 64/70] Cleanup release workflow --- .github/workflows/release.yml | 8 ++------ .github/workflows/snyk.yml | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9887416e..97c08a1e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,14 +15,12 @@ jobs: - name: Git checkout uses: actions/checkout@v4 with: - fetch-depth: 0 - path: awm-relayer submodules: recursive - # The GO_VERSION must be set explicitly to be used by GoReleaser and in the Dockerfile. + # The GO_VERSION must be set explicitly to be used in the Dockerfile. - name: Set Go version run: | - source ./awm-relayer/scripts/versions.sh + source ./scripts/versions.sh echo GO_VERSION=$GO_VERSION >> $GITHUB_ENV - name: Set up Go @@ -71,8 +69,6 @@ jobs: distribution: goreleaser version: latest args: release --clean - workdir: ./awm-relayer/ env: # https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GO_VERSION: ${{ env.GO_VERSION }} diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index a2a8284f..8934ccc2 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -17,7 +17,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - path: awm-relayer submodules: recursive - name: Run Snyk From fd6186279e76efbe23a503f2008eb0ed0646177b Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Mon, 8 Jul 2024 09:23:40 -0400 Subject: [PATCH 65/70] Add osxcross to gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 003022bb..14dd770f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,7 @@ server.log # Foundry outputs cache/ -out/ \ No newline at end of file +out/ + +# Release build outputs +osxcross/ From f3838120ebfc68db664cafe1359809f189cd86f9 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 8 Jul 2024 11:11:42 -0400 Subject: [PATCH 66/70] Decode hex or cb58 --- README.md | 4 ++-- api/relay_message.go | 13 ++++++------- utils/utils.go | 13 +++++++++++++ utils/utils_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 14dcb18d..c886aa65 100644 --- a/README.md +++ b/README.md @@ -199,11 +199,11 @@ The relayer is configured via a JSON file, the path to which is passed in via th `"source-blockchain-id": string` - - cb58-encoded blockchain ID of the source blockchain. + - Hex or cb58-encoded blockchain ID of the source blockchain. `"destination-blockchain-id": string` - - cb58-encoded blockchain ID of the destination blockchain. + - Hex or cb58-encoded blockchain ID of the destination blockchain. `"source-address": string` diff --git a/api/relay_message.go b/api/relay_message.go index 164b2426..f5a6228d 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -5,11 +5,10 @@ import ( "math/big" "net/http" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/awm-relayer/relayer" "github.com/ava-labs/awm-relayer/types" - relayerTypes "github.com/ava-labs/awm-relayer/types" + "github.com/ava-labs/awm-relayer/utils" "github.com/ethereum/go-ethereum/common" "go.uber.org/zap" ) @@ -20,9 +19,9 @@ const ( ) type RelayMessageRequest struct { - // Required. cb58 encoding of the source blockchain ID for the message + // Required. Hex or cb58 encoding of the source blockchain ID for the message BlockchainID string `json:"blockchain-id"` - // Required. cb58 encoding of the warp message ID + // Required. Hex or cb58 encoding of the warp message ID MessageID string `json:"message-id"` // Required. Block number that the message was sent in BlockNum uint64 `json:"block-num"` @@ -64,7 +63,7 @@ func relayMessageAPIHandler(logger logging.Logger, messageCoordinator *relayer.M return } - warpMessageInfo := &relayerTypes.WarpMessageInfo{ + warpMessageInfo := &types.WarpMessageInfo{ SourceAddress: common.HexToAddress(req.SourceAddress), UnsignedMessage: unsignedMessage, } @@ -104,13 +103,13 @@ func relayAPIHandler(logger logging.Logger, messageCoordinator *relayer.MessageC return } - blockchainID, err := ids.FromString(req.BlockchainID) + blockchainID, err := utils.HexOrCB58ToID(req.BlockchainID) if err != nil { logger.Warn("Invalid blockchainID", zap.String("blockchainID", req.BlockchainID)) http.Error(w, "invalid blockchainID: "+err.Error(), http.StatusBadRequest) return } - messageID, err := ids.FromString(req.MessageID) + messageID, err := utils.HexOrCB58ToID(req.MessageID) if err != nil { logger.Warn("Invalid messageID", zap.String("messageID", req.MessageID)) http.Error(w, "invalid messageID: "+err.Error(), http.StatusBadRequest) diff --git a/utils/utils.go b/utils/utils.go index c96a7588..9fac8e08 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ethereum/go-ethereum/common" ) @@ -123,3 +124,15 @@ func StripFromString(input, substring string) string { return strippedString } + +// Converts a '0x'-prefixed hex string or cb58-encoded string to an ID. +func HexOrCB58ToID(s string) (ids.ID, error) { + if strings.HasPrefix(s, "0x") { + bytes, err := hex.DecodeString(SanitizeHexString(s)) + if err != nil { + return ids.ID{}, err + } + return ids.ToID(bytes) + } + return ids.FromString(s) +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 11854e5a..0ace0515 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -10,6 +10,44 @@ import ( "github.com/stretchr/testify/require" ) +func TestHexOrCB58ToID(t *testing.T) { + testCases := []struct { + name string + encoding string + expectedResult string + errorExpected bool + }{ + { + name: "hex conversion", + encoding: "0x7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5", + expectedResult: "yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp", + errorExpected: false, + }, + { + name: "cb58 conversion", + encoding: "yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp", + expectedResult: "yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp", + errorExpected: false, + }, + { + name: "non-prefixed hex", + encoding: "7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5", + errorExpected: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + actualResult, err := HexOrCB58ToID(testCase.encoding) + if testCase.errorExpected { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, testCase.expectedResult, actualResult.String()) + } + }) + } +} + func TestSanitizeHexString(t *testing.T) { testCases := []struct { name string From a238ca8018ccf1ffca13328f85c4a03bfe1ecfb6 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 8 Jul 2024 12:00:22 -0400 Subject: [PATCH 67/70] clarify comments --- README.md | 4 ++-- api/relay_message.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c886aa65..ed610c41 100644 --- a/README.md +++ b/README.md @@ -199,11 +199,11 @@ The relayer is configured via a JSON file, the path to which is passed in via th `"source-blockchain-id": string` - - Hex or cb58-encoded blockchain ID of the source blockchain. + - cb58 encoded or "0x" prefixed Hex encoded blockchain ID of the source blockchain. `"destination-blockchain-id": string` - - Hex or cb58-encoded blockchain ID of the destination blockchain. + - cb58 encoded or "0x" prefixed Hex encoded blockchain ID of the destination blockchain. `"source-address": string` diff --git a/api/relay_message.go b/api/relay_message.go index f5a6228d..a80ae44a 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -19,9 +19,9 @@ const ( ) type RelayMessageRequest struct { - // Required. Hex or cb58 encoding of the source blockchain ID for the message + // Required. cb58 encoding or "0x" prefixed Hex encoding of the source blockchain ID for the message BlockchainID string `json:"blockchain-id"` - // Required. Hex or cb58 encoding of the warp message ID + // Required. cb58 encoding or "0x" prefixed Hex encoding of the warp message ID MessageID string `json:"message-id"` // Required. Block number that the message was sent in BlockNum uint64 `json:"block-num"` From 9618cc7f365a55ca27f1ae1ac553d04f19eb8213 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 8 Jul 2024 16:33:14 -0400 Subject: [PATCH 68/70] Decode hex or cb58 in config --- README.md | 16 ++++++++-------- api/relay_message.go | 4 ++-- config/destination_blockchain.go | 19 +++++-------------- config/source_blockchain.go | 19 +++++++------------ 4 files changed, 22 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index ed610c41..6ca8b531 100644 --- a/README.md +++ b/README.md @@ -199,11 +199,11 @@ The relayer is configured via a JSON file, the path to which is passed in via th `"source-blockchain-id": string` - - cb58 encoded or "0x" prefixed Hex encoded blockchain ID of the source blockchain. + - cb58-encoded or "0x" prefixed hex-encoded blockchain ID of the source blockchain. `"destination-blockchain-id": string` - - cb58 encoded or "0x" prefixed Hex encoded blockchain ID of the destination blockchain. + - cb58-encoded or "0x" prefixed hex-encoded blockchain ID of the destination blockchain. `"source-address": string` @@ -219,11 +219,11 @@ The relayer is configured via a JSON file, the path to which is passed in via th `"subnet-id": string` - - cb58-encoded Subnet ID. + - cb58-encoded or "0x" prefixed hex-encoded Subnet ID. `"blockchain-id": string` - - cb58-encoded blockchain ID. + - cb58-encoded or "0x" prefixed hex-encoded blockchain ID. `"vm": string` @@ -263,11 +263,11 @@ The relayer is configured via a JSON file, the path to which is passed in via th `"subnet-id": string` - - cb58-encoded Subnet ID. + - cb58-encoded or "0x" prefixed hex-encoded Subnet ID. `"blockchain-id": string` - - cb58-encoded blockchain ID. + - cb58-encoded or "0x" prefixed hex-encoded blockchain ID. `"vm": string` @@ -322,8 +322,8 @@ The relayer consists of the following components: - Used to manually relay a Warp message. The body of the request must contain the following JSON: ```json { - "blockchain-id": "", - "message-id": "", + "blockchain-id": "", + "message-id": "", "block-num": "" } ``` diff --git a/api/relay_message.go b/api/relay_message.go index a80ae44a..8b0d479a 100644 --- a/api/relay_message.go +++ b/api/relay_message.go @@ -19,9 +19,9 @@ const ( ) type RelayMessageRequest struct { - // Required. cb58 encoding or "0x" prefixed Hex encoding of the source blockchain ID for the message + // Required. cb58-encoded or "0x" prefixed hex-encoded source blockchain ID for the message BlockchainID string `json:"blockchain-id"` - // Required. cb58 encoding or "0x" prefixed Hex encoding of the warp message ID + // Required. cb58-encoded or "0x" prefixed hex-encoded warp message ID MessageID string `json:"message-id"` // Required. Block number that the message was sent in BlockNum uint64 `json:"block-num"` diff --git a/config/destination_blockchain.go b/config/destination_blockchain.go index fb3c2814..a7bbf970 100644 --- a/config/destination_blockchain.go +++ b/config/destination_blockchain.go @@ -30,16 +30,7 @@ type DestinationBlockchain struct { } // Validatees the destination subnet configuration -func (s *DestinationBlockchain) Validate() error { - if _, err := ids.FromString(s.SubnetID); err != nil { - return fmt.Errorf("invalid subnetID in destination subnet configuration. Provided ID: %s", s.SubnetID) - } - if _, err := ids.FromString(s.BlockchainID); err != nil { - return fmt.Errorf( - "invalid blockchainID in destination subnet configuration. Provided ID: %s", - s.BlockchainID, - ) - } +func (s *DestinationBlockchain) Validate() error { if err := s.RPCEndpoint.Validate(); err != nil { return fmt.Errorf("invalid rpc-endpoint in destination subnet configuration: %w", err) } @@ -63,14 +54,14 @@ func (s *DestinationBlockchain) Validate() error { } // Validate and store the subnet and blockchain IDs for future use - blockchainID, err := ids.FromString(s.BlockchainID) + blockchainID, err := utils.HexOrCB58ToID(s.BlockchainID) if err != nil { - return fmt.Errorf("invalid blockchainID in configuration. error: %w", err) + return fmt.Errorf("invalid blockchainID '%s' in configuration. error: %w", s.BlockchainID, err) } s.blockchainID = blockchainID - subnetID, err := ids.FromString(s.SubnetID) + subnetID, err := utils.HexOrCB58ToID(s.SubnetID) if err != nil { - return fmt.Errorf("invalid subnetID in configuration. error: %w", err) + return fmt.Errorf("invalid subnetID '%s' in configuration. error: %w", s.SubnetID, err) } s.subnetID = subnetID diff --git a/config/source_blockchain.go b/config/source_blockchain.go index fc39ed50..1786d280 100644 --- a/config/source_blockchain.go +++ b/config/source_blockchain.go @@ -37,12 +37,6 @@ type SourceBlockchain struct { // destinationBlockchainIDs. Does not modify the public fields as derived from the configuration passed to the // application, but does initialize private fields available through getters. func (s *SourceBlockchain) Validate(destinationBlockchainIDs *set.Set[string]) error { - if _, err := ids.FromString(s.SubnetID); err != nil { - return fmt.Errorf("invalid subnetID in source subnet configuration. Provided ID: %s", s.SubnetID) - } - if _, err := ids.FromString(s.BlockchainID); err != nil { - return fmt.Errorf("invalid blockchainID in source subnet configuration. Provided ID: %s", s.BlockchainID) - } if err := s.RPCEndpoint.Validate(); err != nil { return fmt.Errorf("invalid rpc-endpoint in source subnet configuration: %w", err) } @@ -79,14 +73,14 @@ func (s *SourceBlockchain) Validate(destinationBlockchainIDs *set.Set[string]) e } // Validate and store the subnet and blockchain IDs for future use - blockchainID, err := ids.FromString(s.BlockchainID) + blockchainID, err := utils.HexOrCB58ToID(s.BlockchainID) if err != nil { - return fmt.Errorf("invalid blockchainID in configuration. error: %w", err) + return fmt.Errorf("invalid blockchainID '%s' in configuration. error: %w", s.BlockchainID, err) } s.blockchainID = blockchainID - subnetID, err := ids.FromString(s.SubnetID) + subnetID, err := utils.HexOrCB58ToID(s.SubnetID) if err != nil { - return fmt.Errorf("invalid subnetID in configuration. error: %w", err) + return fmt.Errorf("invalid subnetID '%s' in configuration. error: %w", s.SubnetID, err) } s.subnetID = subnetID @@ -99,7 +93,7 @@ func (s *SourceBlockchain) Validate(destinationBlockchainIDs *set.Set[string]) e } } for _, dest := range s.SupportedDestinations { - blockchainID, err := ids.FromString(dest.BlockchainID) + blockchainID, err := utils.HexOrCB58ToID(dest.BlockchainID) if err != nil { return fmt.Errorf("invalid blockchainID in configuration. error: %w", err) } @@ -107,7 +101,8 @@ func (s *SourceBlockchain) Validate(destinationBlockchainIDs *set.Set[string]) e return fmt.Errorf( "configured source subnet %s has a supported destination blockchain ID %s that is not configured as a destination blockchain", //nolint:lll s.SubnetID, - blockchainID) + blockchainID, + ) } dest.blockchainID = blockchainID for _, addressStr := range dest.Addresses { From 313a85aca86fb64bff6bcc43127fcfcad996496e Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Mon, 8 Jul 2024 16:58:22 -0400 Subject: [PATCH 69/70] lint --- config/destination_blockchain.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/destination_blockchain.go b/config/destination_blockchain.go index a7bbf970..7146f7ce 100644 --- a/config/destination_blockchain.go +++ b/config/destination_blockchain.go @@ -29,8 +29,8 @@ type DestinationBlockchain struct { blockchainID ids.ID } -// Validatees the destination subnet configuration -func (s *DestinationBlockchain) Validate() error { +// Validates the destination subnet configuration +func (s *DestinationBlockchain) Validate() error { if err := s.RPCEndpoint.Validate(); err != nil { return fmt.Errorf("invalid rpc-endpoint in destination subnet configuration: %w", err) } From 66d5f1d691dd2aa67c073dd148a9cfc7a29f124f Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Tue, 9 Jul 2024 13:36:07 -0400 Subject: [PATCH 70/70] Comment --- utils/utils.go | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/utils.go b/utils/utils.go index 9fac8e08..36b5cd2f 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -126,6 +126,7 @@ func StripFromString(input, substring string) string { } // Converts a '0x'-prefixed hex string or cb58-encoded string to an ID. +// Input length validation is handled by the ids package. func HexOrCB58ToID(s string) (ids.ID, error) { if strings.HasPrefix(s, "0x") { bytes, err := hex.DecodeString(SanitizeHexString(s))