From 51d4e6fd3a499dea0891e67c749dd8a5892d5674 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Mon, 29 Apr 2024 18:29:46 +0000 Subject: [PATCH 01/49] route incoming messages to appRelayer --- main/main.go | 5 +-- peers/app_request_network.go | 25 +++++---------- peers/external_handler.go | 56 ++++++++++++++++++++++++++-------- relayer/application_relayer.go | 9 ++---- relayer/listener.go | 12 +++----- 5 files changed, 60 insertions(+), 47 deletions(-) diff --git a/main/main.go b/main/main.go index 8746f329..5bf34b17 100644 --- a/main/main.go +++ b/main/main.go @@ -103,7 +103,7 @@ func main() { if logLevel <= logging.Debug { networkLogLevel = logLevel } - network, responseChans, err := peers.NewNetwork( + network, err := peers.NewNetwork( networkLogLevel, registerer, &cfg, @@ -222,7 +222,6 @@ func main() { *subnetInfo, pChainClient, network, - responseChans[blockchainID], destinationClients, messageCreator, health, @@ -248,7 +247,6 @@ func runRelayer( sourceSubnetInfo config.SourceBlockchain, pChainClient platformvm.Client, network *peers.AppRequestNetwork, - responseChan chan message.InboundMessage, destinationClients map[ids.ID]vms.DestinationClient, messageCreator message.Creator, relayerHealth *atomic.Bool, @@ -268,7 +266,6 @@ func runRelayer( sourceSubnetInfo, pChainClient, network, - responseChan, destinationClients, messageCreator, relayerHealth, diff --git a/peers/app_request_network.go b/peers/app_request_network.go index cee48d09..8d8f5d3c 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -12,7 +12,6 @@ import ( "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/network" snowVdrs "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/constants" @@ -49,7 +48,7 @@ func NewNetwork( cfg *config.Config, infoClient info.Client, pChainClient platformvm.Client, -) (*AppRequestNetwork, map[ids.ID]chan message.InboundMessage, error) { +) (*AppRequestNetwork, error) { logger := logging.NewLogger( "awm-relayer-p2p", logging.NewWrappedCore( @@ -65,7 +64,7 @@ func NewNetwork( "Failed to get network ID", zap.Error(err), ) - return nil, nil, err + return nil, err } // Create the test network for AppRequests @@ -74,21 +73,13 @@ func NewNetwork( trackedSubnets.Add(sourceBlockchain.GetSubnetID()) } - // Construct a response chan for each chain. Inbound messages will be routed to the proper channel in the handler - responseChans := make(map[ids.ID]chan message.InboundMessage) - for _, sourceBlockchain := range cfg.SourceBlockchains { - responseChan := make(chan message.InboundMessage, InboundMessageChannelSize) - responseChans[sourceBlockchain.GetBlockchainID()] = responseChan - } - responseChansLock := new(sync.RWMutex) - - handler, err := NewRelayerExternalHandler(logger, registerer, responseChans, responseChansLock) + handler, err := NewRelayerExternalHandler(logger, registerer) if err != nil { logger.Error( "Failed to create p2p network handler", zap.Error(err), ) - return nil, nil, err + return nil, err } testNetwork, err := network.NewTestNetwork(logger, networkID, snowVdrs.NewManager(), trackedSubnets, handler) @@ -97,7 +88,7 @@ func NewNetwork( "Failed to create test network", zap.Error(err), ) - return nil, nil, err + return nil, err } validatorClient := validators.NewCanonicalValidatorClient(logger, pChainClient) @@ -118,11 +109,11 @@ func NewNetwork( for _, sourceBlockchain := range cfg.SourceBlockchains { if sourceBlockchain.GetSubnetID() == constants.PrimaryNetworkID { if err := arNetwork.connectToPrimaryNetworkPeers(cfg, sourceBlockchain); err != nil { - return nil, nil, err + return nil, err } } else { if err := arNetwork.connectToNonPrimaryNetworkPeers(cfg, sourceBlockchain); err != nil { - return nil, nil, err + return nil, err } } } @@ -131,7 +122,7 @@ func NewNetwork( testNetwork.Dispatch() }) - return arNetwork, responseChans, nil + return arNetwork, nil } // ConnectPeers connects the network to peers with the given nodeIDs. diff --git a/peers/external_handler.go b/peers/external_handler.go index a5c1a1b2..396bb6f3 100644 --- a/peers/external_handler.go +++ b/peers/external_handler.go @@ -9,7 +9,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" - "github.com/ava-labs/avalanchego/snow/engine/common" + avalancheCommon "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/networking/router" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/logging" @@ -27,16 +27,19 @@ var _ router.ExternalHandler = &RelayerExternalHandler{} type RelayerExternalHandler struct { log logging.Logger responseChansLock *sync.RWMutex - responseChans map[ids.ID]chan message.InboundMessage + responseChans map[uint32]chan message.InboundMessage + responsesCount map[uint32]expectedResponses timeoutManager timer.AdaptiveTimeoutManager } +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. func NewRelayerExternalHandler( logger logging.Logger, registerer prometheus.Registerer, - responseChans map[ids.ID]chan message.InboundMessage, - responseChansLock *sync.RWMutex, ) (*RelayerExternalHandler, error) { // TODO: Leaving this static for now, but we may want to have this as a config option cfg := timer.AdaptiveTimeoutConfig{ @@ -60,8 +63,9 @@ func NewRelayerExternalHandler( return &RelayerExternalHandler{ log: logger, - responseChansLock: responseChansLock, - responseChans: responseChans, + responseChansLock: &sync.RWMutex{}, + responseChans: make(map[uint32]chan message.InboundMessage), + responsesCount: make(map[uint32]expectedResponses), timeoutManager: timeoutManager, }, nil } @@ -80,7 +84,7 @@ func (h *RelayerExternalHandler) HandleInbound(_ context.Context, inboundMessage zap.Stringer("op", inboundMessage.Op()), ) if inboundMessage.Op() == message.AppResponseOp || inboundMessage.Op() == message.AppErrorOp { - h.log.Info("Handling app response", zap.Stringer("from", inboundMessage.NodeID())) + h.log.Debug("Handling app response", zap.Stringer("from", inboundMessage.NodeID())) // Extract the message fields m := inboundMessage.Message() @@ -122,8 +126,11 @@ func (h *RelayerExternalHandler) HandleInbound(_ context.Context, inboundMessage go func(message.InboundMessage, ids.ID) { h.responseChansLock.RLock() defer h.responseChansLock.RUnlock() - - h.responseChans[blockchainID] <- inboundMessage + if responseChan, ok := h.responseChans[requestID]; ok { + responseChan <- inboundMessage + } else { + h.log.Debug("Could not find response channel for request", zap.Uint32("requestID", reqID.RequestID)) + } }(inboundMessage, blockchainID) } else { h.log.Debug("Ignoring message", zap.Stringer("op", inboundMessage.Op())) @@ -150,20 +157,45 @@ func (h *RelayerExternalHandler) Disconnected(nodeID ids.NodeID) { // RegisterRequest registers an AppRequest with the timeout manager. // If RegisterResponse is not called before the timeout, HandleInbound is called with // an internally created AppRequestFailed message. -func (h *RelayerExternalHandler) RegisterRequest(reqID ids.RequestID) { +func (h *RelayerExternalHandler) RegisterRequest(reqID ids.RequestID, numExpectedResponses int) chan message.InboundMessage { + // Create a channel to receive the response + h.responseChansLock.Lock() + defer h.responseChansLock.Unlock() + + responseChan := make(chan message.InboundMessage, numExpectedResponses) + h.responseChans[reqID.RequestID] = responseChan + h.responsesCount[reqID.RequestID] = expectedResponses{ + expected: numExpectedResponses, + } + inMsg := message.InboundAppError( reqID.NodeID, reqID.SourceChainID, reqID.RequestID, - common.ErrTimeout.Code, - common.ErrTimeout.Message, + avalancheCommon.ErrTimeout.Code, + avalancheCommon.ErrTimeout.Message, ) h.timeoutManager.Put(reqID, false, func() { h.HandleInbound(context.Background(), inMsg) }) + return responseChan } // RegisterResponse registers an AppResponse with the timeout manager func (h *RelayerExternalHandler) RegisterResponse(reqID ids.RequestID) { + h.responseChansLock.Lock() + defer h.responseChansLock.Unlock() h.timeoutManager.Remove(reqID) + responses := h.responsesCount[reqID.RequestID] + received := responses.received + 1 + if received == responses.expected { + close(h.responseChans[reqID.RequestID]) + delete(h.responseChans, reqID.RequestID) + delete(h.responsesCount, reqID.RequestID) + } else { + h.responsesCount[reqID.RequestID] = expectedResponses{ + expected: h.responsesCount[reqID.RequestID].expected, + received: received, + } + } } diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index f310bbd5..b11315e1 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -58,7 +58,6 @@ type applicationRelayer struct { metrics *ApplicationRelayerMetrics network *peers.AppRequestNetwork messageCreator message.Creator - responseChan chan message.InboundMessage sourceBlockchain config.SourceBlockchain signingSubnetID ids.ID relayerID database.RelayerID @@ -71,7 +70,6 @@ func newApplicationRelayer( metrics *ApplicationRelayerMetrics, network *peers.AppRequestNetwork, messageCreator message.Creator, - responseChan chan message.InboundMessage, relayerID database.RelayerID, db database.RelayerDatabase, ticker *utils.Ticker, @@ -105,7 +103,6 @@ func newApplicationRelayer( metrics: metrics, network: network, messageCreator: messageCreator, - responseChan: responseChan, sourceBlockchain: sourceBlockchain, relayerID: relayerID, signingSubnetID: signingSubnet, @@ -311,7 +308,7 @@ func (r *applicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval accumulatedSignatureWeight := big.NewInt(0) signatureMap := make(map[int]blsSignatureBuf) - + var responseChan chan message.InboundMessage for attempt := 1; attempt <= maxRelayerQueryAttempts; attempt++ { responsesExpected := len(connectedValidators.ValidatorSet) - len(signatureMap) r.logger.Debug( @@ -346,7 +343,7 @@ func (r *applicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval RequestID: requestID, Op: byte(message.AppResponseOp), } - r.network.Handler.RegisterRequest(reqID) + responseChan = r.network.Handler.RegisterRequest(reqID, vdrSet.Len()) } sentTo := r.network.Network.Send(outMsg, vdrSet, r.sourceBlockchain.GetSubnetID(), subnets.NoOpAllower) @@ -371,7 +368,7 @@ func (r *applicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval // Handle the responses. For each response, we need to call response.OnFinishedHandling() exactly once. // Wrap the loop body in an anonymous function so that we do so on each loop iteration // TODO: In order to run this concurrently, we need to route to each application relayer from the relayer responseChan - for response := range r.responseChan { + for response := range responseChan { r.logger.Debug( "Processing response from node", zap.String("nodeID", response.NodeID().String()), diff --git a/relayer/listener.go b/relayer/listener.go index f3689646..1c8f55fb 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -63,7 +63,6 @@ func NewListener( sourceBlockchain config.SourceBlockchain, pChainClient platformvm.Client, network *peers.AppRequestNetwork, - responseChan chan message.InboundMessage, destinationClients map[ids.ID]vms.DestinationClient, messageCreator message.Creator, relayerHealth *atomic.Bool, @@ -119,7 +118,6 @@ func NewListener( metrics, network, messageCreator, - responseChan, relayerID, db, ticker, @@ -148,7 +146,6 @@ func NewListener( Subscriber: sub, pChainClient: pChainClient, currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision - responseChan: responseChan, contractMessage: vms.NewContractMessage(logger, sourceBlockchain), messageManagers: messageManagers, logger: logger, @@ -261,7 +258,7 @@ func (lstnr *Listener) setAllProcessedBlockHeightsToLatest() error { return nil } -func (lstnr *Listener) NewWarpLogInfo(log types.Log) (*relayerTypes.WarpLogInfo, error) { +func (lstnr *Listener) newWarpLogInfo(log types.Log) (*relayerTypes.WarpLogInfo, error) { if len(log.Topics) != 3 { lstnr.logger.Error( "Log did not have the correct number of topics", @@ -318,7 +315,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { expectedMessages := make(map[database.RelayerID]uint64) var msgsInfo []*parsedMessageInfo for _, warpLog := range block.WarpLogs { - warpLogInfo, err := lstnr.NewWarpLogInfo(warpLog) + warpLogInfo, err := lstnr.newWarpLogInfo(warpLog) if err != nil { lstnr.logger.Error( "Failed to create warp log info", @@ -381,7 +378,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) // TODO try to resubscribe in perpetuity once we have a mechanism for refreshing state // variables such as Quorum values and processing missed blocks. - err = lstnr.ReconnectToSubscriber() + err = lstnr.reconnectToSubscriber() if err != nil { lstnr.logger.Error( "Relayer goroutine exiting.", @@ -402,7 +399,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { } // Sets the listener health status to false while attempting to reconnect. -func (lstnr *Listener) ReconnectToSubscriber() error { +func (lstnr *Listener) reconnectToSubscriber() error { // Attempt to reconnect the subscription err := lstnr.Subscriber.Subscribe(maxResubscribeAttempts) if err != nil { @@ -415,7 +412,6 @@ func (lstnr *Listener) ReconnectToSubscriber() error { } // RouteMessage relays a single warp message to the destination chain. -// Warp message relay requests from the same origin chain are processed serially func (lstnr *Listener) RouteMessage(warpLogInfo *relayerTypes.WarpLogInfo) error { parsedMessageInfo, err := lstnr.parseMessage(warpLogInfo) if err != nil { From a1bdf878e9b432531e0838ddedfa0bc3e31d62d4 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 30 Apr 2024 20:55:47 +0000 Subject: [PATCH 02/49] todo comments --- relayer/listener.go | 4 ++++ vms/evm/subscriber.go | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/relayer/listener.go b/relayer/listener.go index 12e02d40..03e547a4 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -130,6 +130,10 @@ func NewListener( applicationRelayers := make(map[common.Hash]*applicationRelayer) minHeight := uint64(0) + // TODONOW: initialize application relayers in main, and pass list to Listener constructor + // This will allow us to directly access the application relayers from the main goroutine. + // We can eliminate RouteManualWarpMessage and clean up ProcessLogs/encapsulate much of the logic + // within application relayers for _, relayerID := range database.GetSourceBlockchainRelayerIDs(&sourceBlockchain) { height, err := database.CalculateStartingBlockHeight( logger, diff --git a/vms/evm/subscriber.go b/vms/evm/subscriber.go index 438cd33f..0b4e2c16 100644 --- a/vms/evm/subscriber.go +++ b/vms/evm/subscriber.go @@ -81,7 +81,7 @@ func (s *subscriber) ProcessFromHeight(height *big.Int, done chan bool) { zap.String("blockchainID", s.blockchainID.String()), ) if height == nil { - s.logger.Error("cannot process logs from nil height") + s.logger.Error("Cannot process logs from nil height") done <- false return } @@ -113,7 +113,7 @@ func (s *subscriber) ProcessFromHeight(height *big.Int, done chan bool) { err = s.processBlockRange(fromBlock, toBlock) if err != nil { - s.logger.Error("failed to process block range", zap.Error(err)) + s.logger.Error("Failed to process block range", zap.Error(err)) done <- false return } From 3b0c438a9b118caf9ad241f111d191ed87d01728 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 30 Apr 2024 21:26:27 +0000 Subject: [PATCH 03/49] remove catch up results chan --- relayer/listener.go | 22 +--------------------- vms/evm/subscriber.go | 32 +++++++++----------------------- vms/evm/subscriber_test.go | 6 ++---- vms/subscriber.go | 2 +- 4 files changed, 13 insertions(+), 49 deletions(-) diff --git a/relayer/listener.go b/relayer/listener.go index 03e547a4..952d3ddc 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -46,7 +46,6 @@ type Listener struct { messageManagers map[common.Address]messages.MessageManager logger logging.Logger sourceBlockchain config.SourceBlockchain - catchUpResultChan chan bool healthStatus *atomic.Bool globalConfig *config.Config applicationRelayers map[common.Hash]*applicationRelayer @@ -109,14 +108,6 @@ func NewListener( messageManagers[address] = messageManager } - // 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 - // via the subscriber's Subscribe method. As a result, we cannot safely write the - // latest processed block to the database without risking missing a block in a fault - // scenario. - catchUpResultChan := make(chan bool, 1) - currentHeight, err := ethRPCClient.BlockNumber(context.Background()) if err != nil { logger.Error( @@ -190,7 +181,6 @@ func NewListener( messageManagers: messageManagers, logger: logger, sourceBlockchain: sourceBlockchain, - catchUpResultChan: catchUpResultChan, healthStatus: relayerHealth, globalConfig: cfg, applicationRelayers: applicationRelayers, @@ -212,13 +202,12 @@ func NewListener( // 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. - go sub.ProcessFromHeight(big.NewInt(0).SetUint64(minHeight), lstnr.catchUpResultChan) + go sub.ProcessFromHeight(big.NewInt(0).SetUint64(minHeight)) } else { lstnr.logger.Info( "processed-missed-blocks set to false, starting processing from chain head", zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), ) - lstnr.catchUpResultChan <- true } return &lstnr, nil @@ -254,15 +243,6 @@ func (lstnr *Listener) NewWarpLogInfo(log types.Log) (*relayerTypes.WarpLogInfo, func (lstnr *Listener) ProcessLogs(ctx context.Context) error { for { select { - case catchUpResult := <-lstnr.catchUpResultChan: - if !catchUpResult { - lstnr.healthStatus.Store(false) - lstnr.logger.Error( - "Failed to catch up on historical blocks. Exiting listener goroutine.", - zap.String("sourceBlockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - ) - return fmt.Errorf("failed to catch up on historical blocks") - } case block := <-lstnr.Subscriber.Blocks(): // Relay the messages in the block to the destination chains. Continue on failure. lstnr.logger.Info( diff --git a/vms/evm/subscriber.go b/vms/evm/subscriber.go index 0b4e2c16..6f3f2363 100644 --- a/vms/evm/subscriber.go +++ b/vms/evm/subscriber.go @@ -35,7 +35,7 @@ type subscriber struct { ethClient ethclient.Client blockchainID ids.ID blocksChan chan relayerTypes.WarpBlockInfo - headers <-chan *types.Header + headers chan *types.Header sub interfaces.Subscription logger logging.Logger @@ -50,6 +50,7 @@ func NewSubscriber(logger logging.Logger, blockchainID ids.ID, ethClient ethclie ethClient: ethClient, logger: logger, blocksChan: blocks, + headers: make(chan *types.Header, maxClientSubscriptionBuffer), } } @@ -73,8 +74,7 @@ func (s *subscriber) forwardBlocks() { // `MaxBlocksPerRequest`; if processing more than that, multiple eth_getLogs // requests will be made. // Writes true to the done channel when finished, or false if an error occurs -func (s *subscriber) ProcessFromHeight(height *big.Int, done chan bool) { - defer close(done) +func (s *subscriber) ProcessFromHeight(height *big.Int) error { s.logger.Info( "Processing historical logs", zap.String("fromBlockHeight", height.String()), @@ -82,8 +82,7 @@ func (s *subscriber) ProcessFromHeight(height *big.Int, done chan bool) { ) if height == nil { s.logger.Error("Cannot process logs from nil height") - done <- false - return + return fmt.Errorf("cannot process logs from nil height") } // Grab the latest block before filtering logs so we don't miss any before updating the db @@ -94,8 +93,7 @@ func (s *subscriber) ProcessFromHeight(height *big.Int, done chan bool) { zap.String("blockchainID", s.blockchainID.String()), zap.Error(err), ) - done <- false - return + return err } bigLatestBlockHeight := big.NewInt(0).SetUint64(latestBlockHeight) @@ -114,11 +112,10 @@ func (s *subscriber) ProcessFromHeight(height *big.Int, done chan bool) { err = s.processBlockRange(fromBlock, toBlock) if err != nil { s.logger.Error("Failed to process block range", zap.Error(err)) - done <- false - return + return err } } - done <- true + return nil } // Extract Warp logs from the block, if they exist @@ -159,16 +156,7 @@ func (s *subscriber) processBlockRange( ) return err } - blockInfo, err := s.newWarpBlockInfo(header) - if err != nil { - s.logger.Error( - "Failed to get block info", - zap.String("blockchainID", s.blockchainID.String()), - zap.Error(err), - ) - return err - } - s.blocksChan <- *blockInfo + s.headers <- header } return nil } @@ -211,8 +199,7 @@ func (s *subscriber) Subscribe(maxResubscribeAttempts int) error { } func (s *subscriber) subscribe() error { - headers := make(chan *types.Header, maxClientSubscriptionBuffer) - sub, err := s.ethClient.SubscribeNewHead(context.Background(), headers) + sub, err := s.ethClient.SubscribeNewHead(context.Background(), s.headers) if err != nil { s.logger.Error( "Failed to subscribe to logs", @@ -221,7 +208,6 @@ func (s *subscriber) subscribe() error { ) return err } - s.headers = headers s.sub = sub // Forward blocks to the interface channel. Closed when the subscription is cancelled diff --git a/vms/evm/subscriber_test.go b/vms/evm/subscriber_test.go index c7b002cb..98bf4f00 100644 --- a/vms/evm/subscriber_test.go +++ b/vms/evm/subscriber_test.go @@ -90,10 +90,8 @@ func TestProcessFromHeight(t *testing.T) { Number: big.NewInt(i), }, nil).Times(1) } - done := make(chan bool, 1) - subscriberUnderTest.ProcessFromHeight(big.NewInt(tc.input), done) - result := <-done - require.True(t, result) + err := subscriberUnderTest.ProcessFromHeight(big.NewInt(tc.input)) + require.NoError(t, err) }) } } diff --git a/vms/subscriber.go b/vms/subscriber.go index 826ffd5b..2aaa289a 100644 --- a/vms/subscriber.go +++ b/vms/subscriber.go @@ -20,7 +20,7 @@ import ( type Subscriber interface { // ProcessFromHeight processes events from {height} to the latest block. // Writes true to the channel on success, false on failure - ProcessFromHeight(height *big.Int, done chan bool) + ProcessFromHeight(height *big.Int) error // Subscribe registers a subscription. After Subscribe is called, // log events that match [filter] are written to the channel returned From 4142101051fdf69cb815cc6ba86d924feb12bdae Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 30 Apr 2024 21:27:55 +0000 Subject: [PATCH 04/49] Revert "remove catch up results chan" This reverts commit 3b0c438a9b118caf9ad241f111d191ed87d01728. --- relayer/listener.go | 22 +++++++++++++++++++++- vms/evm/subscriber.go | 32 +++++++++++++++++++++++--------- vms/evm/subscriber_test.go | 6 ++++-- vms/subscriber.go | 2 +- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/relayer/listener.go b/relayer/listener.go index 952d3ddc..03e547a4 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -46,6 +46,7 @@ type Listener struct { messageManagers map[common.Address]messages.MessageManager logger logging.Logger sourceBlockchain config.SourceBlockchain + catchUpResultChan chan bool healthStatus *atomic.Bool globalConfig *config.Config applicationRelayers map[common.Hash]*applicationRelayer @@ -108,6 +109,14 @@ func NewListener( messageManagers[address] = messageManager } + // 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 + // via the subscriber's Subscribe method. As a result, we cannot safely write the + // latest processed block to the database without risking missing a block in a fault + // scenario. + catchUpResultChan := make(chan bool, 1) + currentHeight, err := ethRPCClient.BlockNumber(context.Background()) if err != nil { logger.Error( @@ -181,6 +190,7 @@ func NewListener( messageManagers: messageManagers, logger: logger, sourceBlockchain: sourceBlockchain, + catchUpResultChan: catchUpResultChan, healthStatus: relayerHealth, globalConfig: cfg, applicationRelayers: applicationRelayers, @@ -202,12 +212,13 @@ func NewListener( // 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. - go sub.ProcessFromHeight(big.NewInt(0).SetUint64(minHeight)) + go sub.ProcessFromHeight(big.NewInt(0).SetUint64(minHeight), lstnr.catchUpResultChan) } else { lstnr.logger.Info( "processed-missed-blocks set to false, starting processing from chain head", zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), ) + lstnr.catchUpResultChan <- true } return &lstnr, nil @@ -243,6 +254,15 @@ func (lstnr *Listener) NewWarpLogInfo(log types.Log) (*relayerTypes.WarpLogInfo, func (lstnr *Listener) ProcessLogs(ctx context.Context) error { for { select { + case catchUpResult := <-lstnr.catchUpResultChan: + if !catchUpResult { + lstnr.healthStatus.Store(false) + lstnr.logger.Error( + "Failed to catch up on historical blocks. Exiting listener goroutine.", + zap.String("sourceBlockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), + ) + return fmt.Errorf("failed to catch up on historical blocks") + } case block := <-lstnr.Subscriber.Blocks(): // Relay the messages in the block to the destination chains. Continue on failure. lstnr.logger.Info( diff --git a/vms/evm/subscriber.go b/vms/evm/subscriber.go index 6f3f2363..0b4e2c16 100644 --- a/vms/evm/subscriber.go +++ b/vms/evm/subscriber.go @@ -35,7 +35,7 @@ type subscriber struct { ethClient ethclient.Client blockchainID ids.ID blocksChan chan relayerTypes.WarpBlockInfo - headers chan *types.Header + headers <-chan *types.Header sub interfaces.Subscription logger logging.Logger @@ -50,7 +50,6 @@ func NewSubscriber(logger logging.Logger, blockchainID ids.ID, ethClient ethclie ethClient: ethClient, logger: logger, blocksChan: blocks, - headers: make(chan *types.Header, maxClientSubscriptionBuffer), } } @@ -74,7 +73,8 @@ func (s *subscriber) forwardBlocks() { // `MaxBlocksPerRequest`; if processing more than that, multiple eth_getLogs // requests will be made. // Writes true to the done channel when finished, or false if an error occurs -func (s *subscriber) ProcessFromHeight(height *big.Int) error { +func (s *subscriber) ProcessFromHeight(height *big.Int, done chan bool) { + defer close(done) s.logger.Info( "Processing historical logs", zap.String("fromBlockHeight", height.String()), @@ -82,7 +82,8 @@ func (s *subscriber) ProcessFromHeight(height *big.Int) error { ) if height == nil { s.logger.Error("Cannot process logs from nil height") - return fmt.Errorf("cannot process logs from nil height") + done <- false + return } // Grab the latest block before filtering logs so we don't miss any before updating the db @@ -93,7 +94,8 @@ func (s *subscriber) ProcessFromHeight(height *big.Int) error { zap.String("blockchainID", s.blockchainID.String()), zap.Error(err), ) - return err + done <- false + return } bigLatestBlockHeight := big.NewInt(0).SetUint64(latestBlockHeight) @@ -112,10 +114,11 @@ func (s *subscriber) ProcessFromHeight(height *big.Int) error { err = s.processBlockRange(fromBlock, toBlock) if err != nil { s.logger.Error("Failed to process block range", zap.Error(err)) - return err + done <- false + return } } - return nil + done <- true } // Extract Warp logs from the block, if they exist @@ -156,7 +159,16 @@ func (s *subscriber) processBlockRange( ) return err } - s.headers <- header + blockInfo, err := s.newWarpBlockInfo(header) + if err != nil { + s.logger.Error( + "Failed to get block info", + zap.String("blockchainID", s.blockchainID.String()), + zap.Error(err), + ) + return err + } + s.blocksChan <- *blockInfo } return nil } @@ -199,7 +211,8 @@ func (s *subscriber) Subscribe(maxResubscribeAttempts int) error { } func (s *subscriber) subscribe() error { - sub, err := s.ethClient.SubscribeNewHead(context.Background(), s.headers) + headers := make(chan *types.Header, maxClientSubscriptionBuffer) + sub, err := s.ethClient.SubscribeNewHead(context.Background(), headers) if err != nil { s.logger.Error( "Failed to subscribe to logs", @@ -208,6 +221,7 @@ func (s *subscriber) subscribe() error { ) return err } + s.headers = headers s.sub = sub // Forward blocks to the interface channel. Closed when the subscription is cancelled diff --git a/vms/evm/subscriber_test.go b/vms/evm/subscriber_test.go index 98bf4f00..c7b002cb 100644 --- a/vms/evm/subscriber_test.go +++ b/vms/evm/subscriber_test.go @@ -90,8 +90,10 @@ func TestProcessFromHeight(t *testing.T) { Number: big.NewInt(i), }, nil).Times(1) } - err := subscriberUnderTest.ProcessFromHeight(big.NewInt(tc.input)) - require.NoError(t, err) + done := make(chan bool, 1) + subscriberUnderTest.ProcessFromHeight(big.NewInt(tc.input), done) + result := <-done + require.True(t, result) }) } } diff --git a/vms/subscriber.go b/vms/subscriber.go index 2aaa289a..826ffd5b 100644 --- a/vms/subscriber.go +++ b/vms/subscriber.go @@ -20,7 +20,7 @@ import ( type Subscriber interface { // ProcessFromHeight processes events from {height} to the latest block. // Writes true to the channel on success, false on failure - ProcessFromHeight(height *big.Int) error + ProcessFromHeight(height *big.Int, done chan bool) // Subscribe registers a subscription. After Subscribe is called, // log events that match [filter] are written to the channel returned From 01a07e33dd5042e256b16e3b9143d9b28f09a1ac Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 30 Apr 2024 21:29:11 +0000 Subject: [PATCH 05/49] write directly to headers chan --- vms/evm/subscriber.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/vms/evm/subscriber.go b/vms/evm/subscriber.go index 0b4e2c16..6da896fb 100644 --- a/vms/evm/subscriber.go +++ b/vms/evm/subscriber.go @@ -35,7 +35,7 @@ type subscriber struct { ethClient ethclient.Client blockchainID ids.ID blocksChan chan relayerTypes.WarpBlockInfo - headers <-chan *types.Header + headers chan *types.Header sub interfaces.Subscription logger logging.Logger @@ -50,6 +50,7 @@ func NewSubscriber(logger logging.Logger, blockchainID ids.ID, ethClient ethclie ethClient: ethClient, logger: logger, blocksChan: blocks, + headers: make(chan *types.Header, maxClientSubscriptionBuffer), } } @@ -159,16 +160,7 @@ func (s *subscriber) processBlockRange( ) return err } - blockInfo, err := s.newWarpBlockInfo(header) - if err != nil { - s.logger.Error( - "Failed to get block info", - zap.String("blockchainID", s.blockchainID.String()), - zap.Error(err), - ) - return err - } - s.blocksChan <- *blockInfo + s.headers <- header } return nil } @@ -211,8 +203,7 @@ func (s *subscriber) Subscribe(maxResubscribeAttempts int) error { } func (s *subscriber) subscribe() error { - headers := make(chan *types.Header, maxClientSubscriptionBuffer) - sub, err := s.ethClient.SubscribeNewHead(context.Background(), headers) + sub, err := s.ethClient.SubscribeNewHead(context.Background(), s.headers) if err != nil { s.logger.Error( "Failed to subscribe to logs", @@ -221,7 +212,6 @@ func (s *subscriber) subscribe() error { ) return err } - s.headers = headers s.sub = sub // Forward blocks to the interface channel. Closed when the subscription is cancelled From 678fa9267057884da36f1c17dff035e151f35ded Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Thu, 2 May 2024 15:31:21 +0000 Subject: [PATCH 06/49] send to channel before closing --- peers/external_handler.go | 101 +++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/peers/external_handler.go b/peers/external_handler.go index c56e219c..5e8d5076 100644 --- a/peers/external_handler.go +++ b/peers/external_handler.go @@ -80,58 +80,12 @@ func NewRelayerExternalHandler( // 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( - "Receiving message", + "Handling app response", zap.Stringer("op", inboundMessage.Op()), + zap.Stringer("from", inboundMessage.NodeID()), ) if inboundMessage.Op() == message.AppResponseOp || inboundMessage.Op() == message.AppErrorOp { - h.log.Debug("Handling app response", zap.Stringer("from", inboundMessage.NodeID())) - - // Extract the message fields - m := inboundMessage.Message() - - // Get the blockchainID from the message. - // Note: we should NOT call GetSourceBlockchainID; this is for cross-chain messages using the vm2 interface - // For normal app requests messages, the calls result in the same value, but if the relayer handles an - // inbound cross-chain app message, then we would get the incorrect chain ID. - blockchainID, err := message.GetChainID(m) - if err != nil { - h.log.Error("Could not get blockchainID from message") - inboundMessage.OnFinishedHandling() - return - } - sourceBlockchainID, err := message.GetSourceChainID(m) - if err != nil { - h.log.Error("Could not get sourceBlockchainID from message") - inboundMessage.OnFinishedHandling() - return - } - requestID, ok := message.GetRequestID(m) - if !ok { - h.log.Error("Could not get requestID from message") - inboundMessage.OnFinishedHandling() - return - } - - reqID := ids.RequestID{ - NodeID: inboundMessage.NodeID(), - SourceChainID: sourceBlockchainID, - DestinationChainID: blockchainID, - RequestID: requestID, - Op: byte(inboundMessage.Op()), - } - h.RegisterAppResponse(reqID) - - // Route to the appropriate response channel. Do not block on this call, otherwise incoming message handling may be blocked - // OnFinishedHandling is called by the consumer of the response channel - go func(message.InboundMessage, ids.ID) { - h.responseChansLock.RLock() - defer h.responseChansLock.RUnlock() - if responseChan, ok := h.responseChans[requestID]; ok { - responseChan <- inboundMessage - } else { - h.log.Debug("Could not find response channel for request", zap.Uint32("requestID", reqID.RequestID)) - } - }(inboundMessage, blockchainID) + h.RegisterAppResponse(inboundMessage) } else { h.log.Debug("Ignoring message", zap.Stringer("op", inboundMessage.Op())) inboundMessage.OnFinishedHandling() @@ -159,6 +113,8 @@ func (h *RelayerExternalHandler) RegisterRequestID(requestID uint32, numExpected h.responseChansLock.Lock() defer h.responseChansLock.Unlock() + h.log.Debug("Registering request ID", zap.Uint32("requestID", requestID)) + responseChan := make(chan message.InboundMessage, numExpectedResponses) h.responseChans[requestID] = responseChan h.responsesCount[requestID] = expectedResponses{ @@ -184,10 +140,55 @@ func (h *RelayerExternalHandler) RegisterAppRequest(reqID ids.RequestID) { } // RegisterResponse registers an AppResponse with the timeout manager -func (h *RelayerExternalHandler) RegisterAppResponse(reqID ids.RequestID) { +func (h *RelayerExternalHandler) RegisterAppResponse(inboundMessage message.InboundMessage) { h.responseChansLock.Lock() defer h.responseChansLock.Unlock() + + // Extract the message fields + m := inboundMessage.Message() + + // Get the blockchainID from the message. + // Note: we should NOT call GetSourceBlockchainID; this is for cross-chain messages using the vm2 interface + // For normal app requests messages, the calls result in the same value, but if the relayer handles an + // inbound cross-chain app message, then we would get the incorrect chain ID. + blockchainID, err := message.GetChainID(m) + if err != nil { + h.log.Error("Could not get blockchainID from message") + inboundMessage.OnFinishedHandling() + return + } + sourceBlockchainID, err := message.GetSourceChainID(m) + if err != nil { + h.log.Error("Could not get sourceBlockchainID from message") + inboundMessage.OnFinishedHandling() + return + } + requestID, ok := message.GetRequestID(m) + if !ok { + h.log.Error("Could not get requestID from message") + inboundMessage.OnFinishedHandling() + return + } + + reqID := ids.RequestID{ + NodeID: inboundMessage.NodeID(), + SourceChainID: sourceBlockchainID, + DestinationChainID: blockchainID, + RequestID: requestID, + Op: byte(inboundMessage.Op()), + } + + // Register the response with the timeout manager h.timeoutManager.Remove(reqID) + + // Dispatch to the appropriate response channel + if responseChan, ok := h.responseChans[requestID]; ok { + responseChan <- inboundMessage + } else { + h.log.Debug("Could not find response channel for request", zap.Uint32("requestID", requestID)) + } + + // Check for the expected number of responses responses := h.responsesCount[reqID.RequestID] received := responses.received + 1 if received == responses.expected { From e8371e38a3234e79bcab49d59e19bf6611d897dc Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Thu, 2 May 2024 18:28:36 +0000 Subject: [PATCH 07/49] relay messages async - wip --- relayer/listener.go | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/relayer/listener.go b/relayer/listener.go index 3a96338f..f80bee5c 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -8,6 +8,7 @@ import ( "fmt" "math/big" "math/rand" + "sync" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" @@ -36,6 +37,7 @@ 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 messageManagers map[common.Address]messages.MessageManager @@ -180,6 +182,7 @@ func NewListener( ) lstnr := Listener{ Subscriber: sub, + requestIDLock: &sync.Mutex{}, currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision contractMessage: vms.NewContractMessage(logger, sourceBlockchain), messageManagers: messageManagers, @@ -524,24 +527,28 @@ func (lstnr *Listener) parseMessage(warpLogInfo *relayerTypes.WarpLogInfo) ( } func (lstnr *Listener) dispatchToApplicationRelayer(parsedMessageInfo *parsedMessageInfo, blockNumber uint64) error { + // TODONOW: app relayers should manage their requestIDs. They should be distributed to minimize conflicts + lstnr.requestIDLock.Lock() + defer lstnr.requestIDLock.Unlock() // TODO: Add a config option to use the Warp API, instead of hardcoding to the app request network here - err := parsedMessageInfo.applicationRelayer.relayMessage( + go parsedMessageInfo.applicationRelayer.relayMessage( parsedMessageInfo.unsignedMessage, lstnr.currentRequestID, parsedMessageInfo.messageManager, blockNumber, true, ) - if err != nil { - lstnr.logger.Error( - "Failed to run application relayer", - zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - zap.String("warpMessageID", parsedMessageInfo.unsignedMessage.ID().String()), - zap.Error(err), - ) - } + // if err != nil { + // lstnr.logger.Error( + // "Failed to run application relayer", + // zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), + // zap.String("warpMessageID", parsedMessageInfo.unsignedMessage.ID().String()), + // zap.Error(err), + // ) + // } // Increment the request ID for the next message relay request lstnr.currentRequestID++ - return err + // return err + return nil } From 252b0276467e3a79fcdf0a31327bfcc266892167 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Thu, 2 May 2024 18:49:31 +0000 Subject: [PATCH 08/49] listener composed of app relayers --- main/main.go | 85 +++++++++++++++++++++++++++++++--- relayer/application_relayer.go | 28 +++++------ relayer/listener.go | 82 ++++---------------------------- 3 files changed, 100 insertions(+), 95 deletions(-) diff --git a/main/main.go b/main/main.go index 57afdf21..7c4e2857 100644 --- a/main/main.go +++ b/main/main.go @@ -24,6 +24,8 @@ import ( 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" + "github.com/ethereum/go-ethereum/common" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/atomic" @@ -235,7 +237,7 @@ func runRelayer( metrics *relayer.ApplicationRelayerMetrics, db database.RelayerDatabase, ticker *utils.Ticker, - sourceSubnetInfo config.SourceBlockchain, + sourceBlockchain config.SourceBlockchain, network *peers.AppRequestNetwork, destinationClients map[ids.ID]vms.DestinationClient, messageCreator message.Creator, @@ -243,36 +245,105 @@ func runRelayer( manualWarpMessages []*relayerTypes.WarpLogInfo, cfg *config.Config, ) error { + // Create the application relayers logger.Info( - "Creating relayer", - zap.String("originBlockchainID", sourceSubnetInfo.BlockchainID), + "Creating application relayers", + zap.String("originBlockchainID", sourceBlockchain.BlockchainID), ) + ethClient, err := ethclient.Dial(sourceBlockchain.RPCEndpoint) + if err != nil { + logger.Error( + "Failed to connect to node via RPC", + zap.String("blockchainID", sourceBlockchain.BlockchainID), + zap.Error(err), + ) + return err + } + currentHeight, err := ethClient.BlockNumber(context.Background()) + if err != nil { + logger.Error( + "Failed to get current block height", + zap.Error(err), + ) + return err + } + applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) + minHeight := uint64(0) + + for _, relayerID := range database.GetSourceBlockchainRelayerIDs(&sourceBlockchain) { + height, err := database.CalculateStartingBlockHeight( + logger, + db, + relayerID, + sourceBlockchain.ProcessHistoricalBlocksFromHeight, + currentHeight, + ) + if err != nil { + logger.Error( + "Failed to calculate starting block height", + zap.String("relayerID", relayerID.ID.String()), + zap.Error(err), + ) + return err + } + if minHeight == 0 || height < minHeight { + minHeight = height + } + applicationRelayer, err := relayer.NewApplicationRelayer( + logger, + metrics, + network, + messageCreator, + relayerID, + db, + ticker, + sourceBlockchain, + height, + cfg, + ) + if err != nil { + logger.Error( + "Failed to create application relayer", + zap.String("relayerID", relayerID.ID.String()), + zap.Error(err), + ) + return err + } + applicationRelayers[relayerID.ID] = applicationRelayer + } + logger.Info( + "Creating listener", + zap.String("originBlockchainID", sourceBlockchain.BlockchainID), + ) listener, err := relayer.NewListener( logger, metrics, db, ticker, - sourceSubnetInfo, + sourceBlockchain, network, destinationClients, messageCreator, relayerHealth, cfg, + applicationRelayers, + minHeight, + ethClient, ) if err != nil { return fmt.Errorf("failed to create listener instance: %w", err) } logger.Info( "Created listener", - zap.String("blockchainID", sourceSubnetInfo.BlockchainID), + zap.String("blockchainID", sourceBlockchain.BlockchainID), ) // Send any messages that were specified in the configuration for _, warpMessage := range manualWarpMessages { logger.Info( "Relaying manual Warp message", - zap.String("blockchainID", sourceSubnetInfo.BlockchainID), + zap.String("blockchainID", sourceBlockchain.BlockchainID), zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMsgBytes)), ) err := listener.RouteManualWarpMessage(warpMessage) @@ -288,7 +359,7 @@ func runRelayer( logger.Info( "Listener initialized. Listening for messages to relay.", - zap.String("originBlockchainID", sourceSubnetInfo.BlockchainID), + zap.String("originBlockchainID", sourceBlockchain.BlockchainID), ) // Wait for logs from the subscribed node diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index dd033dc7..d8f6c58b 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -49,11 +49,11 @@ var ( errNotEnoughConnectedStake = errors.New("failed to connect to a threshold of stake") ) -// applicationRelayers are created for each warp message to be relayed. +// ApplicationRelayers are created for each warp message to be relayed. // They collect signatures from validators, aggregate them, // and send the signed warp message to the destination chain. -// Each applicationRelayer runs in its own goroutine. -type applicationRelayer struct { +// Each ApplicationRelayer runs in its own goroutine. +type ApplicationRelayer struct { logger logging.Logger metrics *ApplicationRelayerMetrics network *peers.AppRequestNetwork @@ -65,7 +65,7 @@ type applicationRelayer struct { checkpointManager *checkpoint.CheckpointManager } -func newApplicationRelayer( +func NewApplicationRelayer( logger logging.Logger, metrics *ApplicationRelayerMetrics, network *peers.AppRequestNetwork, @@ -76,7 +76,7 @@ func newApplicationRelayer( sourceBlockchain config.SourceBlockchain, startingHeight uint64, cfg *config.Config, -) (*applicationRelayer, error) { +) (*ApplicationRelayer, error) { quorum, err := cfg.GetWarpQuorum(relayerID.DestinationBlockchainID) if err != nil { logger.Error( @@ -100,7 +100,7 @@ func newApplicationRelayer( checkpointManager := checkpoint.NewCheckpointManager(logger, db, sub, relayerID, startingHeight) checkpointManager.Run() - ar := applicationRelayer{ + ar := ApplicationRelayer{ logger: logger, metrics: metrics, network: network, @@ -115,7 +115,7 @@ func newApplicationRelayer( return &ar, nil } -func (r *applicationRelayer) relayMessage( +func (r *ApplicationRelayer) relayMessage( unsignedMessage *avalancheWarp.UnsignedMessage, requestID uint32, messageManager messages.MessageManager, @@ -188,7 +188,7 @@ 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") // TODO: To properly support this, we should provide a dedicated Warp API endpoint in the config uri := utils.StripFromString(r.sourceBlockchain.RPCEndpoint, "/ext") @@ -249,7 +249,7 @@ func (r *applicationRelayer) createSignedMessage(unsignedMessage *avalancheWarp. } // 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) { +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") connectedValidators, err := r.network.ConnectToCanonicalValidators(r.signingSubnetID) if err != nil { @@ -489,7 +489,7 @@ func (r *applicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval // 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( +func (r *ApplicationRelayer) isValidSignatureResponse( unsignedMessage *avalancheWarp.UnsignedMessage, response message.InboundMessage, pubKey *bls.PublicKey, @@ -554,7 +554,7 @@ func (r *applicationRelayer) isValidSignatureResponse( // 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) { +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() @@ -587,7 +587,7 @@ func (r *applicationRelayer) aggregateSignatures(signatureMap map[int]blsSignatu // Metrics // -func (r *applicationRelayer) incSuccessfulRelayMessageCount() { +func (r *ApplicationRelayer) incSuccessfulRelayMessageCount() { r.metrics.successfulRelayMessageCount. WithLabelValues( r.relayerID.DestinationBlockchainID.String(), @@ -595,7 +595,7 @@ func (r *applicationRelayer) incSuccessfulRelayMessageCount() { r.sourceBlockchain.GetSubnetID().String()).Inc() } -func (r *applicationRelayer) incFailedRelayMessageCount(failureReason string) { +func (r *ApplicationRelayer) incFailedRelayMessageCount(failureReason string) { r.metrics.failedRelayMessageCount. WithLabelValues( r.relayerID.DestinationBlockchainID.String(), @@ -604,7 +604,7 @@ func (r *applicationRelayer) incFailedRelayMessageCount(failureReason string) { failureReason).Inc() } -func (r *applicationRelayer) setCreateSignedMessageLatencyMS(latency float64) { +func (r *ApplicationRelayer) setCreateSignedMessageLatencyMS(latency float64) { r.metrics.createSignedMessageLatencyMS. WithLabelValues( r.relayerID.DestinationBlockchainID.String(), diff --git a/relayer/listener.go b/relayer/listener.go index f80bee5c..6250f49a 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -46,7 +46,7 @@ type Listener struct { catchUpResultChan chan bool healthStatus *atomic.Bool globalConfig *config.Config - applicationRelayers map[common.Hash]*applicationRelayer + applicationRelayers map[common.Hash]*ApplicationRelayer ethClient ethclient.Client } @@ -61,6 +61,9 @@ func NewListener( messageCreator message.Creator, relayerHealth *atomic.Bool, cfg *config.Config, + applicationRelayers map[common.Hash]*ApplicationRelayer, + startingHeight uint64, + ethClient ethclient.Client, ) (*Listener, error) { blockchainID, err := ids.FromString(sourceBlockchain.BlockchainID) if err != nil { @@ -81,16 +84,6 @@ func NewListener( } sub := vms.NewSubscriber(logger, config.ParseVM(sourceBlockchain.VM), blockchainID, ethWSClient) - ethRPCClient, err := ethclient.Dial(sourceBlockchain.RPCEndpoint) - if err != nil { - logger.Error( - "Failed to connect to node via RPC", - zap.String("blockchainID", blockchainID.String()), - zap.Error(err), - ) - return nil, err - } - // Create message managers for each supported message protocol messageManagers := make(map[common.Address]messages.MessageManager) for addressStr, config := range sourceBlockchain.MessageContracts { @@ -114,65 +107,6 @@ func NewListener( // scenario. catchUpResultChan := make(chan bool, 1) - currentHeight, err := ethRPCClient.BlockNumber(context.Background()) - if err != nil { - logger.Error( - "Failed to get current block height", - zap.Error(err), - ) - return nil, err - } - - // Create the application relayers - applicationRelayers := make(map[common.Hash]*applicationRelayer) - minHeight := uint64(0) - - // TODONOW: initialize application relayers in main, and pass list to Listener constructor - // This will allow us to directly access the application relayers from the main goroutine. - // We can eliminate RouteManualWarpMessage and clean up ProcessLogs/encapsulate much of the logic - // within application relayers - for _, relayerID := range database.GetSourceBlockchainRelayerIDs(&sourceBlockchain) { - height, err := database.CalculateStartingBlockHeight( - logger, - db, - relayerID, - sourceBlockchain.ProcessHistoricalBlocksFromHeight, - currentHeight, - ) - if err != nil { - logger.Error( - "Failed to calculate starting block height", - zap.String("relayerID", relayerID.ID.String()), - zap.Error(err), - ) - return nil, err - } - if minHeight == 0 || height < minHeight { - minHeight = height - } - applicationRelayer, err := newApplicationRelayer( - logger, - metrics, - network, - messageCreator, - relayerID, - db, - ticker, - sourceBlockchain, - height, - cfg, - ) - if err != nil { - logger.Error( - "Failed to create application relayer", - zap.String("relayerID", relayerID.ID.String()), - zap.Error(err), - ) - return nil, err - } - applicationRelayers[relayerID.ID] = applicationRelayer - } - logger.Info( "Creating relayer", zap.String("subnetID", sourceBlockchain.GetSubnetID().String()), @@ -192,7 +126,7 @@ func NewListener( healthStatus: relayerHealth, globalConfig: cfg, applicationRelayers: applicationRelayers, - ethClient: ethRPCClient, + ethClient: ethClient, } // Open the subscription. We must do this before processing any missed messages, otherwise we may miss an incoming message @@ -210,7 +144,7 @@ func NewListener( // 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. - go sub.ProcessFromHeight(big.NewInt(0).SetUint64(minHeight), lstnr.catchUpResultChan) + go sub.ProcessFromHeight(big.NewInt(0).SetUint64(startingHeight), lstnr.catchUpResultChan) } else { lstnr.logger.Info( "processed-missed-blocks set to false, starting processing from chain head", @@ -403,7 +337,7 @@ func (lstnr *Listener) getApplicationRelayer( destinationBlockchainID ids.ID, destinationAddress common.Address, messageManager messages.MessageManager, -) *applicationRelayer { +) *ApplicationRelayer { // Check for an exact match applicationRelayerID := database.CalculateRelayerID( sourceBlockchainID, @@ -462,7 +396,7 @@ func (lstnr *Listener) getApplicationRelayer( type parsedMessageInfo struct { unsignedMessage *avalancheWarp.UnsignedMessage messageManager messages.MessageManager - applicationRelayer *applicationRelayer + applicationRelayer *ApplicationRelayer } func (lstnr *Listener) parseMessage(warpLogInfo *relayerTypes.WarpLogInfo) ( From f386d119e8d509ece5a0cfcf3b54f05344160356 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Thu, 2 May 2024 22:18:27 +0000 Subject: [PATCH 09/49] appRelayer maintains block status --- main/main.go | 6 +- relayer/application_relayer.go | 61 ++++++++++++++++++ relayer/listener.go | 112 ++++----------------------------- 3 files changed, 76 insertions(+), 103 deletions(-) diff --git a/main/main.go b/main/main.go index 7c4e2857..b74fc422 100644 --- a/main/main.go +++ b/main/main.go @@ -340,21 +340,23 @@ func runRelayer( ) // 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("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMsgBytes)), ) - err := listener.RouteManualWarpMessage(warpMessage) + appRelayer, err := listener.RegisterMessageWithAppRelayer(warpMessage) if err != nil { logger.Error( - "Failed to relay manual Warp message. Continuing.", + "Failed to parse manual Warp message. Continuing.", zap.Error(err), zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMsgBytes)), ) continue } + appRelayer.ProcessHeight(0) } logger.Info( diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index d8f6c58b..209e65e0 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -8,6 +8,8 @@ import ( "context" "errors" "math/big" + "math/rand" + "sync" "time" "github.com/ava-labs/avalanchego/ids" @@ -63,6 +65,14 @@ type ApplicationRelayer struct { relayerID database.RelayerID warpQuorum config.WarpQuorum checkpointManager *checkpoint.CheckpointManager + currentRequestID uint32 + lock *sync.RWMutex + pendingMessages map[uint64][]pendingMessage +} + +type pendingMessage struct { + unsignedMessage *avalancheWarp.UnsignedMessage + messageManager messages.MessageManager } func NewApplicationRelayer( @@ -110,11 +120,62 @@ func NewApplicationRelayer( signingSubnetID: signingSubnet, warpQuorum: quorum, checkpointManager: checkpointManager, + currentRequestID: rand.Uint32(), // TODONOW: pass via ctor + lock: &sync.RWMutex{}, + pendingMessages: make(map[uint64][]pendingMessage), } return &ar, nil } +func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, unsignedMessage *avalancheWarp.UnsignedMessage, messageManager messages.MessageManager) { + r.lock.Lock() + defer r.lock.Unlock() + r.logger.Debug( + "Registering message for relay", + zap.Uint64("height", height), + zap.String("relayerID", r.relayerID.ID.String()), + ) + r.pendingMessages[height] = append(r.pendingMessages[height], pendingMessage{ + unsignedMessage: unsignedMessage, + messageManager: messageManager, + }) +} + +func (r *ApplicationRelayer) PrepareHeight(height uint64) { + r.lock.RLock() + defer r.lock.RUnlock() + r.logger.Debug( + "Preparing height to relay", + zap.Uint64("height", height), + zap.Int("numMessages", len(r.pendingMessages[height])), + zap.String("relayerID", r.relayerID.ID.String()), + ) + r.checkpointManager.PrepareHeight(height, uint64(len(r.pendingMessages[height]))) +} + +func (r *ApplicationRelayer) ProcessHeight(height uint64) { + r.lock.Lock() + defer r.lock.Unlock() + r.logger.Debug( + "Processing height", + zap.Uint64("height", height), + zap.Int("numMessages", len(r.pendingMessages[height])), + zap.String("relayerID", r.relayerID.ID.String()), + ) + for _, pendingMessage := range r.pendingMessages[height] { + go r.relayMessage( + pendingMessage.unsignedMessage, + r.currentRequestID, + pendingMessage.messageManager, + height, + true, + ) + r.currentRequestID++ + } +} + +// TODONOW: this should write an error to a channel, which the caller should handle func (r *ApplicationRelayer) relayMessage( unsignedMessage *avalancheWarp.UnsignedMessage, requestID uint32, diff --git a/relayer/listener.go b/relayer/listener.go index 6250f49a..6b93f0fa 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -13,7 +13,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/utils/logging" - avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/database" "github.com/ava-labs/awm-relayer/messages" @@ -207,8 +206,8 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { // This is so that the number of messages to be processed can be registered with the database before // any messages are processed. // The second pass dispatches the messages to the application relayers for processing - expectedMessages := make(map[database.RelayerID]uint64) - var msgsInfo []*parsedMessageInfo + // expectedMessages := make(map[database.RelayerID]uint64) + // var msgsInfo []*parsedMessageInfo for _, warpLog := range block.WarpLogs { warpLogInfo, err := relayerTypes.NewWarpLogInfo(warpLog) if err != nil { @@ -218,7 +217,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) continue } - msgInfo, err := lstnr.parseMessage(warpLogInfo) + _, err = lstnr.RegisterMessageWithAppRelayer(warpLogInfo) if err != nil { lstnr.logger.Error( "Failed to parse message", @@ -227,38 +226,10 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) continue } - if msgInfo == nil { - lstnr.logger.Debug( - "Application relayer not found. Skipping message relay.", - zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - ) - continue - } - msgsInfo = append(msgsInfo, msgInfo) - expectedMessages[msgInfo.applicationRelayer.relayerID]++ } for _, appRelayer := range lstnr.applicationRelayers { - // Prepare the each application relayer's database key with the number - // of expected messages. If no messages are found in the above loop, then - // totalMessages will be 0 - totalMessages := expectedMessages[appRelayer.relayerID] - appRelayer.checkpointManager.PrepareHeight(block.BlockNumber, totalMessages) - } - for _, msgInfo := range msgsInfo { - lstnr.logger.Info( - "Relaying message", - zap.String("sourceBlockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - zap.String("warpMessageID", msgInfo.unsignedMessage.ID().String()), - ) - err := lstnr.dispatchToApplicationRelayer(msgInfo, block.BlockNumber) - if err != nil { - lstnr.logger.Error( - "Error relaying message", - zap.String("sourceBlockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - zap.Error(err), - ) - continue - } + appRelayer.PrepareHeight(block.BlockNumber) + appRelayer.ProcessHeight(block.BlockNumber) } case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) @@ -302,29 +273,6 @@ func (lstnr *Listener) reconnectToSubscriber() error { return nil } -// RouteMessage relays a single warp message to the destination chain. -// Warp message relay requests from the same origin chain are processed serially -func (lstnr *Listener) RouteManualWarpMessage(warpLogInfo *relayerTypes.WarpLogInfo) error { - parsedMessageInfo, err := lstnr.parseMessage(warpLogInfo) - if err != nil { - lstnr.logger.Error( - "Failed to parse message", - zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - zap.Error(err), - ) - return err - } - if parsedMessageInfo == nil { - lstnr.logger.Debug( - "Application relayer not found. Skipping message relay.", - zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - ) - return nil - } - - return lstnr.dispatchToApplicationRelayer(parsedMessageInfo, 0) -} - // 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 @@ -391,16 +339,8 @@ func (lstnr *Listener) getApplicationRelayer( return nil } -// Helper type and function to extract the information needed to relay a message -// From the raw log information -type parsedMessageInfo struct { - unsignedMessage *avalancheWarp.UnsignedMessage - messageManager messages.MessageManager - applicationRelayer *ApplicationRelayer -} - -func (lstnr *Listener) parseMessage(warpLogInfo *relayerTypes.WarpLogInfo) ( - *parsedMessageInfo, +func (lstnr *Listener) RegisterMessageWithAppRelayer(warpLogInfo *relayerTypes.WarpLogInfo) ( + *ApplicationRelayer, error, ) { // Check that the warp message is from a supported message protocol contract address. @@ -443,46 +383,16 @@ func (lstnr *Listener) parseMessage(warpLogInfo *relayerTypes.WarpLogInfo) ( zap.String("warpMessageID", unsignedMessage.ID().String()), ) - applicationRelayer := lstnr.getApplicationRelayer( + appRelayer := lstnr.getApplicationRelayer( sourceBlockchainID, originSenderAddress, destinationBlockchainID, destinationAddress, messageManager, ) - if applicationRelayer == nil { + if appRelayer == nil { return nil, nil } - return &parsedMessageInfo{ - unsignedMessage: unsignedMessage, - messageManager: messageManager, - applicationRelayer: applicationRelayer, - }, nil -} - -func (lstnr *Listener) dispatchToApplicationRelayer(parsedMessageInfo *parsedMessageInfo, blockNumber uint64) error { - // TODONOW: app relayers should manage their requestIDs. They should be distributed to minimize conflicts - lstnr.requestIDLock.Lock() - defer lstnr.requestIDLock.Unlock() - // TODO: Add a config option to use the Warp API, instead of hardcoding to the app request network here - go parsedMessageInfo.applicationRelayer.relayMessage( - parsedMessageInfo.unsignedMessage, - lstnr.currentRequestID, - parsedMessageInfo.messageManager, - blockNumber, - true, - ) - // if err != nil { - // lstnr.logger.Error( - // "Failed to run application relayer", - // zap.String("blockchainID", lstnr.sourceBlockchain.GetBlockchainID().String()), - // zap.String("warpMessageID", parsedMessageInfo.unsignedMessage.ID().String()), - // zap.Error(err), - // ) - // } - - // Increment the request ID for the next message relay request - lstnr.currentRequestID++ - // return err - return nil + appRelayer.RegisterMessageAtHeight(0, unsignedMessage, messageManager) + return appRelayer, nil } From b5fca25127584ba1c608cef6ea37bd9a474716b3 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Fri, 3 May 2024 14:29:51 +0000 Subject: [PATCH 10/49] combine prepare and process height --- main/main.go | 4 ++-- relayer/application_relayer.go | 23 ++++++++++------------- relayer/listener.go | 18 ++++++------------ 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/main/main.go b/main/main.go index b74fc422..2ed02cb3 100644 --- a/main/main.go +++ b/main/main.go @@ -347,7 +347,7 @@ func runRelayer( zap.String("blockchainID", sourceBlockchain.BlockchainID), zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMsgBytes)), ) - appRelayer, err := listener.RegisterMessageWithAppRelayer(warpMessage) + appRelayer, err := listener.RegisterMessageWithAppRelayer(0, warpMessage) if err != nil { logger.Error( "Failed to parse manual Warp message. Continuing.", @@ -356,7 +356,7 @@ func runRelayer( ) continue } - appRelayer.ProcessHeight(0) + appRelayer.ProcessHeight(0, false) } logger.Info( diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 209e65e0..3b05e38c 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -128,6 +128,7 @@ func NewApplicationRelayer( return &ar, nil } +// Registers a message to be relayed at a specific height by adding it to the pending messages list at that height. func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, unsignedMessage *avalancheWarp.UnsignedMessage, messageManager messages.MessageManager) { r.lock.Lock() defer r.lock.Unlock() @@ -142,19 +143,10 @@ func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, unsignedMess }) } -func (r *ApplicationRelayer) PrepareHeight(height uint64) { - r.lock.RLock() - defer r.lock.RUnlock() - r.logger.Debug( - "Preparing height to relay", - zap.Uint64("height", height), - zap.Int("numMessages", len(r.pendingMessages[height])), - zap.String("relayerID", r.relayerID.ID.String()), - ) - r.checkpointManager.PrepareHeight(height, uint64(len(r.pendingMessages[height]))) -} - -func (r *ApplicationRelayer) ProcessHeight(height uint64) { +// Processes all pending messages at a specific height. +// If checkpoint is true, Ppepares the number of messages to be relayed at a specific height with the checkpoint manager. +// This effectively marks the height as eligible for writing to the database once all messages are relayed. +func (r *ApplicationRelayer) ProcessHeight(height uint64, checkpoint bool) { r.lock.Lock() defer r.lock.Unlock() r.logger.Debug( @@ -163,7 +155,11 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64) { zap.Int("numMessages", len(r.pendingMessages[height])), zap.String("relayerID", r.relayerID.ID.String()), ) + if checkpoint { + r.checkpointManager.PrepareHeight(height, uint64(len(r.pendingMessages[height]))) + } for _, pendingMessage := range r.pendingMessages[height] { + // TODONOW: if relaying fails, we can keep the pending message in the list, and retry later go r.relayMessage( pendingMessage.unsignedMessage, r.currentRequestID, @@ -173,6 +169,7 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64) { ) r.currentRequestID++ } + delete(r.pendingMessages, height) } // TODONOW: this should write an error to a channel, which the caller should handle diff --git a/relayer/listener.go b/relayer/listener.go index 6b93f0fa..69e038a4 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -201,13 +201,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { zap.Uint64("blockNumber", block.BlockNumber), ) - // Iterate over the Warp logs in two passes: - // The first pass extracts the information needed to relay from the log, but does not initiate relaying - // This is so that the number of messages to be processed can be registered with the database before - // any messages are processed. - // The second pass dispatches the messages to the application relayers for processing - // expectedMessages := make(map[database.RelayerID]uint64) - // var msgsInfo []*parsedMessageInfo + // Register each message in the block with the appropriate application relayer for _, warpLog := range block.WarpLogs { warpLogInfo, err := relayerTypes.NewWarpLogInfo(warpLog) if err != nil { @@ -217,7 +211,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) continue } - _, err = lstnr.RegisterMessageWithAppRelayer(warpLogInfo) + _, err = lstnr.RegisterMessageWithAppRelayer(block.BlockNumber, warpLogInfo) if err != nil { lstnr.logger.Error( "Failed to parse message", @@ -227,9 +221,9 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { continue } } + // Initiate message relay of all registered messages for _, appRelayer := range lstnr.applicationRelayers { - appRelayer.PrepareHeight(block.BlockNumber) - appRelayer.ProcessHeight(block.BlockNumber) + appRelayer.ProcessHeight(block.BlockNumber, true) } case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) @@ -339,7 +333,7 @@ func (lstnr *Listener) getApplicationRelayer( return nil } -func (lstnr *Listener) RegisterMessageWithAppRelayer(warpLogInfo *relayerTypes.WarpLogInfo) ( +func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpLogInfo *relayerTypes.WarpLogInfo) ( *ApplicationRelayer, error, ) { @@ -393,6 +387,6 @@ func (lstnr *Listener) RegisterMessageWithAppRelayer(warpLogInfo *relayerTypes.W if appRelayer == nil { return nil, nil } - appRelayer.RegisterMessageAtHeight(0, unsignedMessage, messageManager) + appRelayer.RegisterMessageAtHeight(height, unsignedMessage, messageManager) return appRelayer, nil } From 34eda3fc1faa554cfb4994c8c80d614cc9a47b9b Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Fri, 3 May 2024 18:49:54 +0000 Subject: [PATCH 11/49] refactor message manager and warpblockinfo --- main/main.go | 25 ++- messages/message_manager.go | 44 +---- .../off-chain-registry/message_manager.go | 47 ++++-- messages/teleporter/message_manager.go | 159 ++++++++---------- relayer/application_relayer.go | 55 +++--- relayer/listener.go | 62 ++++--- tests/e2e_test.go | 31 ++-- tests/utils/utils.go | 36 ++++ types/types.go | 53 ++++-- 9 files changed, 295 insertions(+), 217 deletions(-) diff --git a/main/main.go b/main/main.go index 2ed02cb3..7d25465d 100644 --- a/main/main.go +++ b/main/main.go @@ -21,6 +21,7 @@ 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" @@ -177,13 +178,20 @@ func main() { ticker := utils.NewTicker(cfg.DBWriteIntervalSeconds) go ticker.Run() - manualWarpMessages := make(map[ids.ID][]*relayerTypes.WarpLogInfo) + manualWarpMessages := make(map[ids.ID][]*relayerTypes.WarpMessageInfo) for _, msg := range cfg.ManualWarpMessages { sourceBlockchainID := msg.GetSourceBlockchainID() - - warpLogInfo := relayerTypes.WarpLogInfo{ - SourceAddress: msg.GetSourceAddress(), - UnsignedMsgBytes: msg.GetUnsignedMessageBytes(), + unsignedMsg, err := types.UnpackWarpMessage(msg.GetUnsignedMessageBytes()) + if err != nil { + logger.Error( + "Failed to unpack manual Warp message", + zap.Error(err), + ) + panic(err) + } + warpLogInfo := relayerTypes.WarpMessageInfo{ + SourceAddress: msg.GetSourceAddress(), + UnsignedMessage: unsignedMsg, } manualWarpMessages[sourceBlockchainID] = append(manualWarpMessages[sourceBlockchainID], &warpLogInfo) } @@ -242,7 +250,7 @@ func runRelayer( destinationClients map[ids.ID]vms.DestinationClient, messageCreator message.Creator, relayerHealth *atomic.Bool, - manualWarpMessages []*relayerTypes.WarpLogInfo, + manualWarpMessages []*relayerTypes.WarpMessageInfo, cfg *config.Config, ) error { // Create the application relayers @@ -340,19 +348,18 @@ func runRelayer( ) // 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("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMsgBytes)), + zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), ) appRelayer, err := listener.RegisterMessageWithAppRelayer(0, warpMessage) if err != nil { logger.Error( "Failed to parse manual Warp message. Continuing.", zap.Error(err), - zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMsgBytes)), + zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), ) continue } diff --git a/messages/message_manager.go b/messages/message_manager.go index 00902777..043eab24 100644 --- a/messages/message_manager.go +++ b/messages/message_manager.go @@ -6,63 +6,35 @@ package messages import ( - "fmt" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/awm-relayer/config" - 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/vms" "github.com/ethereum/go-ethereum/common" ) // MessageManager is specific to each message protocol. The interface handles choosing which messages to send // for each message protocol, and performs the sending to the destination chain. type MessageManager interface { + NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (MessageHandler, error) +} + +type MessageHandler interface { // ShouldSendMessage returns true if the message should be sent to the destination chain // If an error is returned, the boolean should be ignored by the caller. - ShouldSendMessage(unsignedMessage *warp.UnsignedMessage, destinationBlockchainID ids.ID) (bool, error) + ShouldSendMessage(destinationBlockchainID ids.ID) (bool, error) // 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, destinationBlockchainID ids.ID) error // GetMessageRoutingInfo returns the source chain ID, origin sender address, destination chain ID, and destination address - GetMessageRoutingInfo(unsignedMessage *warp.UnsignedMessage) ( + GetMessageRoutingInfo() ( ids.ID, common.Address, ids.ID, common.Address, error, ) -} -// NewMessageManager constructs a MessageManager for a particular message protocol, defined by the message protocol address and config -// Note that DestinationClients may be invoked concurrently by many MessageManagers, so it is assumed that they are implemented in a thread-safe way -func NewMessageManager( - logger logging.Logger, - messageProtocolAddress common.Address, - messageProtocolConfig config.MessageProtocolConfig, - destinationClients map[ids.ID]vms.DestinationClient, -) (MessageManager, error) { - format := messageProtocolConfig.MessageFormat - switch config.ParseMessageProtocol(format) { - case config.TELEPORTER: - return teleporter.NewMessageManager( - logger, - messageProtocolAddress, - messageProtocolConfig, - destinationClients, - ) - case config.OFF_CHAIN_REGISTRY: - return offchainregistry.NewMessageManager( - logger, - messageProtocolConfig, - destinationClients, - ) - default: - return nil, fmt.Errorf("invalid message format %s", format) - } + // GetUnsignedMessage returns the unsigned message + GetUnsignedMessage() *warp.UnsignedMessage } diff --git a/messages/off-chain-registry/message_manager.go b/messages/off-chain-registry/message_manager.go index 81dbbafc..4968839b 100644 --- a/messages/off-chain-registry/message_manager.go +++ b/messages/off-chain-registry/message_manager.go @@ -13,6 +13,7 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/warp" warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/awm-relayer/config" + "github.com/ava-labs/awm-relayer/messages" "github.com/ava-labs/awm-relayer/vms" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/ethclient" @@ -36,6 +37,12 @@ type messageManager struct { registryAddress common.Address } +type messageHandler struct { + logger logging.Logger + unsignedMessage *warp.UnsignedMessage + messageManager *messageManager +} + func NewMessageManager( logger logging.Logger, messageProtocolConfig config.MessageProtocolConfig, @@ -67,10 +74,22 @@ func NewMessageManager( }, nil } +func (m *messageManager) NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (messages.MessageHandler, error) { + return &messageHandler{ + logger: m.logger, + unsignedMessage: unsignedMessage, + messageManager: m, + }, nil +} + +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. -func (m *messageManager) ShouldSendMessage(unsignedMessage *warp.UnsignedMessage, destinationBlockchainID ids.ID) (bool, error) { - addressedPayload, err := warpPayload.ParseAddressedCall(unsignedMessage.Payload) +func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool, error) { + addressedPayload, err := warpPayload.ParseAddressedCall(m.unsignedMessage.Payload) if err != nil { m.logger.Error( "Failed parsing addressed payload", @@ -86,17 +105,17 @@ func (m *messageManager) ShouldSendMessage(unsignedMessage *warp.UnsignedMessage ) return false, err } - if destination != m.registryAddress { + if destination != m.messageManager.registryAddress { m.logger.Info( "Message is not intended for the configured registry", zap.String("destination", destination.String()), - zap.String("configuredRegistry", m.registryAddress.String()), + zap.String("configuredRegistry", m.messageManager.registryAddress.String()), ) return false, nil } // Get the correct destination client from the global map - destinationClient, ok := m.destinationClients[destinationBlockchainID] + destinationClient, ok := m.messageManager.destinationClients[destinationBlockchainID] if !ok { return false, fmt.Errorf("relayer not configured to deliver to destination. destinationBlockchainID=%s", destinationBlockchainID.String()) } @@ -106,7 +125,7 @@ func (m *messageManager) ShouldSendMessage(unsignedMessage *warp.UnsignedMessage } // Check if the version is already registered in the TeleporterRegistry contract. - registry, err := teleporterregistry.NewTeleporterRegistryCaller(m.registryAddress, client) + registry, err := teleporterregistry.NewTeleporterRegistryCaller(m.messageManager.registryAddress, client) if err != nil { m.logger.Error( "Failed to create TeleporterRegistry caller", @@ -134,9 +153,9 @@ func (m *messageManager) ShouldSendMessage(unsignedMessage *warp.UnsignedMessage return false, nil } -func (m *messageManager) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { +func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { // Get the correct destination client from the global map - destinationClient, ok := m.destinationClients[destinationBlockchainID] + destinationClient, ok := m.messageManager.destinationClients[destinationBlockchainID] if !ok { return fmt.Errorf("relayer not configured to deliver to destination. DestinationBlockchainID=%s", destinationBlockchainID) } @@ -153,7 +172,7 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, destinationBlo return err } - err = destinationClient.SendTx(signedMessage, m.registryAddress.Hex(), addProtocolVersionGasLimit, callData) + err = destinationClient.SendTx(signedMessage, m.messageManager.registryAddress.Hex(), addProtocolVersionGasLimit, callData) if err != nil { m.logger.Error( "Failed to send tx.", @@ -171,14 +190,14 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, destinationBlo return nil } -func (m *messageManager) GetMessageRoutingInfo(unsignedMessage *warp.UnsignedMessage) ( +func (m *messageHandler) GetMessageRoutingInfo() ( ids.ID, common.Address, ids.ID, common.Address, error, ) { - addressedPayload, err := warpPayload.ParseAddressedCall(unsignedMessage.Payload) + addressedPayload, err := warpPayload.ParseAddressedCall(m.unsignedMessage.Payload) if err != nil { m.logger.Error( "Failed parsing addressed payload", @@ -186,9 +205,9 @@ func (m *messageManager) GetMessageRoutingInfo(unsignedMessage *warp.UnsignedMes ) return ids.ID{}, common.Address{}, ids.ID{}, common.Address{}, err } - return unsignedMessage.SourceChainID, + return m.unsignedMessage.SourceChainID, common.BytesToAddress(addressedPayload.SourceAddress), - unsignedMessage.SourceChainID, - m.registryAddress, + m.unsignedMessage.SourceChainID, + m.messageManager.registryAddress, nil } diff --git a/messages/teleporter/message_manager.go b/messages/teleporter/message_manager.go index 98803e98..a0e9b4e2 100644 --- a/messages/teleporter/message_manager.go +++ b/messages/teleporter/message_manager.go @@ -7,12 +7,12 @@ import ( "encoding/json" "fmt" - "github.com/ava-labs/avalanchego/cache" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/platformvm/warp" warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/awm-relayer/config" + "github.com/ava-labs/awm-relayer/messages" "github.com/ava-labs/awm-relayer/vms" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/ethclient" @@ -28,15 +28,17 @@ const ( ) type messageManager struct { - messageConfig Config - protocolAddress common.Address - - // We parse teleporter messages in ShouldSendMessage, cache them to be reused in SendMessage - // The cache is keyed by the Warp message ID, NOT the Teleporter message ID - teleporterMessageCache *cache.LRU[ids.ID, *teleportermessenger.TeleporterMessage] - destinationClients map[ids.ID]vms.DestinationClient + messageConfig Config + protocolAddress common.Address + destinationClients map[ids.ID]vms.DestinationClient + logger logging.Logger +} - logger logging.Logger +type messageHandler struct { + logger logging.Logger + teleporterMessage *teleportermessenger.TeleporterMessage + unsignedMessage *warp.UnsignedMessage + messageManager *messageManager } func NewMessageManager( @@ -64,14 +66,29 @@ func NewMessageManager( ) return nil, err } - teleporterMessageCache := &cache.LRU[ids.ID, *teleportermessenger.TeleporterMessage]{Size: teleporterMessageCacheSize} return &messageManager{ - messageConfig: messageConfig, - protocolAddress: messageProtocolAddress, - teleporterMessageCache: teleporterMessageCache, - destinationClients: destinationClients, - logger: logger, + messageConfig: messageConfig, + protocolAddress: messageProtocolAddress, + destinationClients: destinationClients, + logger: logger, + }, nil +} + +func (m *messageManager) NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (messages.MessageHandler, error) { + teleporterMessage, err := m.parseTeleporterMessage(unsignedMessage) + if err != nil { + m.logger.Error( + "Failed to parse teleporter message.", + zap.String("warpMessageID", unsignedMessage.ID().String()), + ) + return nil, err + } + return &messageHandler{ + logger: m.logger, + teleporterMessage: teleporterMessage, + unsignedMessage: unsignedMessage, + messageManager: m, }, nil } @@ -89,77 +106,62 @@ func isAllowedRelayer(allowedRelayers []common.Address, eoa common.Address) bool return false } -func (m *messageManager) GetMessageRoutingInfo(unsignedMessage *warp.UnsignedMessage) ( +func (m *messageHandler) GetUnsignedMessage() *warp.UnsignedMessage { + return m.unsignedMessage +} + +func (m *messageHandler) GetMessageRoutingInfo() ( ids.ID, common.Address, ids.ID, common.Address, error, ) { - teleporterMessage, err := m.parseTeleporterMessage(unsignedMessage) - if err != nil { - m.logger.Error( - "Failed to parse teleporter message.", - zap.String("warpMessageID", unsignedMessage.ID().String()), - ) - return ids.ID{}, common.Address{}, ids.ID{}, common.Address{}, err - } - return unsignedMessage.SourceChainID, - teleporterMessage.OriginSenderAddress, - teleporterMessage.DestinationBlockchainID, - teleporterMessage.DestinationAddress, + return m.unsignedMessage.SourceChainID, + m.teleporterMessage.OriginSenderAddress, + m.teleporterMessage.DestinationBlockchainID, + m.teleporterMessage.DestinationAddress, nil } // ShouldSendMessage returns true if the message should be sent to the destination chain -func (m *messageManager) ShouldSendMessage(unsignedMessage *warp.UnsignedMessage, destinationBlockchainID ids.ID) (bool, error) { - teleporterMessage, err := m.parseTeleporterMessage(unsignedMessage) - if err != nil { - m.logger.Error( - "Failed get teleporter message.", - zap.String("destinationBlockchainID", destinationBlockchainID.String()), - zap.String("warpMessageID", unsignedMessage.ID().String()), - zap.Error(err), - ) - return false, err - } - +func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool, error) { // Get the correct destination client from the global map - destinationClient, ok := m.destinationClients[destinationBlockchainID] + destinationClient, ok := m.messageManager.destinationClients[destinationBlockchainID] if !ok { // This shouldn't occur, since we already check this in Listener.RouteMessage. Return an error in this case. return false, fmt.Errorf("relayer not configured to deliver to destination. destinationBlockchainID=%s", destinationBlockchainID.String()) } teleporterMessageID, err := teleporterUtils.CalculateMessageID( - m.protocolAddress, - unsignedMessage.SourceChainID, + m.messageManager.protocolAddress, + m.unsignedMessage.SourceChainID, destinationBlockchainID, - teleporterMessage.MessageNonce, + m.teleporterMessage.MessageNonce, ) if err != nil { return false, fmt.Errorf("failed to calculate Teleporter message ID: %w", err) } senderAddress := destinationClient.SenderAddress() - if !isAllowedRelayer(teleporterMessage.AllowedRelayerAddresses, senderAddress) { + if !isAllowedRelayer(m.teleporterMessage.AllowedRelayerAddresses, senderAddress) { m.logger.Info( "Relayer EOA not allowed to deliver this message.", zap.String("destinationBlockchainID", destinationBlockchainID.String()), - zap.String("warpMessageID", unsignedMessage.ID().String()), + zap.String("warpMessageID", m.unsignedMessage.ID().String()), zap.String("teleporterMessageID", teleporterMessageID.String()), ) return false, nil } // Check if the message has already been delivered to the destination chain - teleporterMessenger := m.getTeleporterMessenger(destinationClient) + teleporterMessenger := m.messageManager.getTeleporterMessenger(destinationClient) delivered, err := teleporterMessenger.MessageReceived(&bind.CallOpts{}, teleporterMessageID) if err != nil { m.logger.Error( "Failed to check if message has been delivered to destination chain.", zap.String("destinationBlockchainID", destinationBlockchainID.String()), - zap.String("warpMessageID", unsignedMessage.ID().String()), + zap.String("warpMessageID", m.unsignedMessage.ID().String()), zap.String("teleporterMessageID", teleporterMessageID.String()), zap.Error(err), ) @@ -179,28 +181,18 @@ func (m *messageManager) ShouldSendMessage(unsignedMessage *warp.UnsignedMessage // 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 *messageManager) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { - teleporterMessage, err := m.parseTeleporterMessage(&signedMessage.UnsignedMessage) - if err != nil { - m.logger.Error( - "Failed get teleporter message.", - zap.String("destinationBlockchainID", destinationBlockchainID.String()), - zap.String("warpMessageID", signedMessage.ID().String()), - ) - return err - } - +func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { // Get the correct destination client from the global map - destinationClient, ok := m.destinationClients[destinationBlockchainID] + destinationClient, ok := m.messageManager.destinationClients[destinationBlockchainID] if !ok { return fmt.Errorf("relayer not configured to deliver to destination. DestinationBlockchainID=%s", destinationBlockchainID) } teleporterMessageID, err := teleporterUtils.CalculateMessageID( - m.protocolAddress, + m.messageManager.protocolAddress, signedMessage.SourceChainID, destinationBlockchainID, - teleporterMessage.MessageNonce, + m.teleporterMessage.MessageNonce, ) if err != nil { return fmt.Errorf("failed to calculate Teleporter message ID: %w", err) @@ -222,7 +214,7 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, destinationBlo ) return err } - gasLimit, err := gasUtils.CalculateReceiveMessageGasLimit(numSigners, teleporterMessage.RequiredGasLimit) + gasLimit, err := gasUtils.CalculateReceiveMessageGasLimit(numSigners, m.teleporterMessage.RequiredGasLimit) if err != nil { m.logger.Error( "Gas limit required overflowed uint64 max. not relaying message", @@ -233,7 +225,7 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, destinationBlo return 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.messageConfig.RewardAddress)) + callData, err := teleportermessenger.PackReceiveCrossChainMessage(0, common.HexToAddress(m.messageManager.messageConfig.RewardAddress)) if err != nil { m.logger.Error( "Failed packing receiveCrossChainMessage call data", @@ -244,7 +236,7 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, destinationBlo return err } - err = destinationClient.SendTx(signedMessage, m.protocolAddress.Hex(), gasLimit, callData) + err = destinationClient.SendTx(signedMessage, m.messageManager.protocolAddress.Hex(), gasLimit, callData) if err != nil { m.logger.Error( "Failed to send tx.", @@ -267,32 +259,23 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, destinationBlo // parseTeleporterMessage returns the Warp message's corresponding Teleporter message from the cache if it exists. // Otherwise parses the Warp message payload. func (m *messageManager) parseTeleporterMessage(unsignedMessage *warp.UnsignedMessage) (*teleportermessenger.TeleporterMessage, error) { - // Check if the message has already been parsed - teleporterMessage, ok := m.teleporterMessageCache.Get(unsignedMessage.ID()) - if !ok { - // If not, parse the message - m.logger.Debug( - "Teleporter message to send not in cache. Extracting from signed warp message.", + addressedPayload, err := warpPayload.ParseAddressedCall(unsignedMessage.Payload) + if err != nil { + m.logger.Error( + "Failed parsing addressed payload", + zap.Error(err), + ) + return nil, err + } + teleporterMessage, err := teleportermessenger.UnpackTeleporterMessage(addressedPayload.Payload) + if err != nil { + m.logger.Error( + "Failed unpacking teleporter message.", zap.String("warpMessageID", unsignedMessage.ID().String()), ) - addressedPayload, err := warpPayload.ParseAddressedCall(unsignedMessage.Payload) - if err != nil { - m.logger.Error( - "Failed parsing addressed payload", - zap.Error(err), - ) - return nil, err - } - teleporterMessage, err = teleportermessenger.UnpackTeleporterMessage(addressedPayload.Payload) - if err != nil { - m.logger.Error( - "Failed unpacking teleporter message.", - zap.String("warpMessageID", unsignedMessage.ID().String()), - ) - return nil, err - } - m.teleporterMessageCache.Put(unsignedMessage.ID(), teleporterMessage) + return nil, err } + return teleporterMessage, nil } diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 3b05e38c..2fa1953d 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -30,6 +30,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" + "go.uber.org/zap" ) @@ -67,12 +68,7 @@ type ApplicationRelayer struct { checkpointManager *checkpoint.CheckpointManager currentRequestID uint32 lock *sync.RWMutex - pendingMessages map[uint64][]pendingMessage -} - -type pendingMessage struct { - unsignedMessage *avalancheWarp.UnsignedMessage - messageManager messages.MessageManager + pendingMessages map[uint64][]messages.MessageHandler } func NewApplicationRelayer( @@ -122,14 +118,37 @@ func NewApplicationRelayer( checkpointManager: checkpointManager, currentRequestID: rand.Uint32(), // TODONOW: pass via ctor lock: &sync.RWMutex{}, - pendingMessages: make(map[uint64][]pendingMessage), + pendingMessages: make(map[uint64][]messages.MessageHandler), } return &ar, nil } +// // Processes all messages in the provided block info. +// // Checkpoints the height with the checkpoint manager when all messages are relayed. +// // It is up to the caller to populate [block] with messages intended to be processed by this application relayer. +// func (r *ApplicationRelayer) ProcessBlock(block relayerTypes.WarpBlockInfo) error { +// r.checkpointManager.PrepareHeight(block.BlockNumber, uint64(len(block.Messages))) +// for _, message := range block.Messages { +// r.ProcessMessage(message) +// } +// } + +// // Relays a message to the destination chain. Does not checkpoint the height. +// func (r *ApplicationRelayer) ProcessMessage(message *relayerTypes.WarpMessageInfo, height uint64) error { +// r.lock.Lock() +// defer r.lock.Unlock() +// go r.relayMessage( +// message.UnsignedMessage, +// r.currentRequestID, +// pendingMessage.messageManager, +// height, +// true, +// ) +// } + // Registers a message to be relayed at a specific height by adding it to the pending messages list at that height. -func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, unsignedMessage *avalancheWarp.UnsignedMessage, messageManager messages.MessageManager) { +func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, messageHandler messages.MessageHandler) { r.lock.Lock() defer r.lock.Unlock() r.logger.Debug( @@ -137,14 +156,11 @@ func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, unsignedMess zap.Uint64("height", height), zap.String("relayerID", r.relayerID.ID.String()), ) - r.pendingMessages[height] = append(r.pendingMessages[height], pendingMessage{ - unsignedMessage: unsignedMessage, - messageManager: messageManager, - }) + r.pendingMessages[height] = append(r.pendingMessages[height], messageHandler) } // Processes all pending messages at a specific height. -// If checkpoint is true, Ppepares the number of messages to be relayed at a specific height with the checkpoint manager. +// If checkpoint is true, Prepares the number of messages to be relayed at a specific height with the checkpoint manager. // This effectively marks the height as eligible for writing to the database once all messages are relayed. func (r *ApplicationRelayer) ProcessHeight(height uint64, checkpoint bool) { r.lock.Lock() @@ -158,12 +174,11 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, checkpoint bool) { if checkpoint { r.checkpointManager.PrepareHeight(height, uint64(len(r.pendingMessages[height]))) } - for _, pendingMessage := range r.pendingMessages[height] { + for _, messageHandler := range r.pendingMessages[height] { // TODONOW: if relaying fails, we can keep the pending message in the list, and retry later go r.relayMessage( - pendingMessage.unsignedMessage, r.currentRequestID, - pendingMessage.messageManager, + messageHandler, height, true, ) @@ -174,13 +189,12 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, checkpoint bool) { // TODONOW: this should write an error to a channel, which the caller should handle func (r *ApplicationRelayer) relayMessage( - unsignedMessage *avalancheWarp.UnsignedMessage, requestID uint32, - messageManager messages.MessageManager, + messageHandler messages.MessageHandler, blockNumber uint64, useAppRequestNetwork bool, ) error { - shouldSend, err := messageManager.ShouldSendMessage(unsignedMessage, r.relayerID.DestinationBlockchainID) + shouldSend, err := messageHandler.ShouldSendMessage(r.relayerID.DestinationBlockchainID) if err != nil { r.logger.Error( "Failed to check if message should be sent", @@ -194,6 +208,7 @@ func (r *ApplicationRelayer) relayMessage( r.checkpointManager.Finished(blockNumber) return nil } + unsignedMessage := messageHandler.GetUnsignedMessage() startCreateSignedMessageTime := time.Now() // Query nodes on the origin chain for signatures, and construct the signed warp message. @@ -223,7 +238,7 @@ func (r *ApplicationRelayer) relayMessage( // create signed message latency (ms) r.setCreateSignedMessageLatencyMS(float64(time.Since(startCreateSignedMessageTime).Milliseconds())) - err = messageManager.SendMessage(signedMessage, r.relayerID.DestinationBlockchainID) + err = messageHandler.SendMessage(signedMessage, r.relayerID.DestinationBlockchainID) if err != nil { r.logger.Error( "Failed to send warp message", diff --git a/relayer/listener.go b/relayer/listener.go index 69e038a4..5ca3eaea 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -16,6 +16,8 @@ import ( "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" relayerTypes "github.com/ava-labs/awm-relayer/types" "github.com/ava-labs/awm-relayer/utils" @@ -85,9 +87,30 @@ func NewListener( // Create message managers for each supported message protocol messageManagers := make(map[common.Address]messages.MessageManager) - for addressStr, config := range sourceBlockchain.MessageContracts { + for addressStr, cfg := range sourceBlockchain.MessageContracts { address := common.HexToAddress(addressStr) - messageManager, err := messages.NewMessageManager(logger, address, config, destinationClients) + format := cfg.MessageFormat + var ( + m messages.MessageManager + err error + ) + switch config.ParseMessageProtocol(format) { + case config.TELEPORTER: + m, err = teleporter.NewMessageManager( + logger, + address, + cfg, + destinationClients, + ) + case config.OFF_CHAIN_REGISTRY: + m, err = offchainregistry.NewMessageManager( + logger, + cfg, + destinationClients, + ) + default: + m, err = nil, fmt.Errorf("invalid message format %s", format) + } if err != nil { logger.Error( "Failed to create message manager", @@ -95,7 +118,7 @@ func NewListener( ) return nil, err } - messageManagers[address] = messageManager + messageManagers[address] = m } // Marks when the listener has finished the catch-up process on startup. @@ -185,6 +208,8 @@ 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( @@ -202,15 +227,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) // Register each message in the block with the appropriate application relayer - for _, warpLog := range block.WarpLogs { - warpLogInfo, err := relayerTypes.NewWarpLogInfo(warpLog) - if err != nil { - lstnr.logger.Error( - "Failed to create warp log info", - zap.Error(err), - ) - continue - } + for _, warpLogInfo := range block.Messages { _, err = lstnr.RegisterMessageWithAppRelayer(block.BlockNumber, warpLogInfo) if err != nil { lstnr.logger.Error( @@ -278,7 +295,6 @@ func (lstnr *Listener) getApplicationRelayer( originSenderAddress common.Address, destinationBlockchainID ids.ID, destinationAddress common.Address, - messageManager messages.MessageManager, ) *ApplicationRelayer { // Check for an exact match applicationRelayerID := database.CalculateRelayerID( @@ -333,33 +349,32 @@ func (lstnr *Listener) getApplicationRelayer( return nil } -func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpLogInfo *relayerTypes.WarpLogInfo) ( +func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpMessageInfo *relayerTypes.WarpMessageInfo) ( *ApplicationRelayer, error, ) { // Check that the warp message is from a supported message protocol contract address. - messageManager, supportedMessageProtocol := lstnr.messageManagers[warpLogInfo.SourceAddress] + messageManager, supportedMessageProtocol := lstnr.messageManagers[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", warpLogInfo.SourceAddress.Hex()), + zap.String("protocolAddress", warpMessageInfo.SourceAddress.Hex()), ) return nil, nil } - - // Unpack the VM message bytes into a Warp message - unsignedMessage, err := lstnr.contractMessage.UnpackWarpMessage(warpLogInfo.UnsignedMsgBytes) + messageHandler, err := messageManager.NewMessageHandler(warpMessageInfo.UnsignedMessage) if err != nil { lstnr.logger.Error( - "Failed to unpack sender message", + "Failed to create message handler", zap.Error(err), ) return nil, err } + // Fetch the message delivery data - sourceBlockchainID, originSenderAddress, destinationBlockchainID, destinationAddress, err := messageManager.GetMessageRoutingInfo(unsignedMessage) + sourceBlockchainID, originSenderAddress, destinationBlockchainID, destinationAddress, err := messageHandler.GetMessageRoutingInfo() if err != nil { lstnr.logger.Error( "Failed to get message routing information", @@ -374,7 +389,7 @@ func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpLogInfo zap.String("originSenderAddress", originSenderAddress.String()), zap.String("destinationBlockchainID", destinationBlockchainID.String()), zap.String("destinationAddress", destinationAddress.String()), - zap.String("warpMessageID", unsignedMessage.ID().String()), + zap.String("warpMessageID", warpMessageInfo.UnsignedMessage.ID().String()), ) appRelayer := lstnr.getApplicationRelayer( @@ -382,11 +397,10 @@ func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpLogInfo originSenderAddress, destinationBlockchainID, destinationAddress, - messageManager, ) if appRelayer == nil { return nil, nil } - appRelayer.RegisterMessageAtHeight(height, unsignedMessage, messageManager) + appRelayer.RegisterMessageAtHeight(height, messageHandler) return appRelayer, nil } diff --git a/tests/e2e_test.go b/tests/e2e_test.go index b5c6ffa1..4d06011f 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -62,19 +62,22 @@ var _ = ginkgo.AfterSuite(func() { }) var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { - ginkgo.It("Manually Provided Message", func() { - ManualMessage(localNetworkInstance) - }) - ginkgo.It("Basic Relay", func() { - BasicRelay(localNetworkInstance) - }) - ginkgo.It("Teleporter Registry", func() { - TeleporterRegistry(localNetworkInstance) - }) - ginkgo.It("Shared Database", func() { - SharedDatabaseAccess(localNetworkInstance) - }) - ginkgo.It("Allowed Addresses", func() { - AllowedAddresses(localNetworkInstance) + // ginkgo.It("Manually Provided Message", func() { + // ManualMessage(localNetworkInstance) + // }) + // ginkgo.It("Basic Relay", func() { + // BasicRelay(localNetworkInstance) + // }) + // ginkgo.It("Teleporter Registry", func() { + // TeleporterRegistry(localNetworkInstance) + // }) + // ginkgo.It("Shared Database", func() { + // SharedDatabaseAccess(localNetworkInstance) + // }) + // ginkgo.It("Allowed Addresses", func() { + // AllowedAddresses(localNetworkInstance) + // }) + ginkgo.It("Queue Messages", func() { + QueueMessages(localNetworkInstance) }) }) diff --git a/tests/utils/utils.go b/tests/utils/utils.go index c18f952e..3d3acab3 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -209,6 +209,42 @@ func FundRelayers( } } +func SendBasicTeleporterMessageAsync( + ctx context.Context, + source interfaces.SubnetTestInfo, + destination interfaces.SubnetTestInfo, + fundedKey *ecdsa.PrivateKey, + destinationAddress common.Address, + ids chan<- ids.ID, +) { + input := teleportermessenger.TeleporterMessageInput{ + DestinationBlockchainID: destination.BlockchainID, + DestinationAddress: destinationAddress, + FeeInfo: teleportermessenger.TeleporterFeeInfo{ + FeeTokenAddress: common.Address{}, + Amount: big.NewInt(0), + }, + RequiredGasLimit: big.NewInt(1), + AllowedRelayerAddresses: []common.Address{}, + Message: []byte{1, 2, 3, 4}, + } + + // Send a transaction to the Teleporter contract + log.Info( + "Sending teleporter transaction", + "sourceBlockchainID", source.BlockchainID, + "destinationBlockchainID", destination.BlockchainID, + ) + _, teleporterMessageID := teleporterTestUtils.SendCrossChainMessageAndWaitForAcceptance( + ctx, + source, + destination, + input, + fundedKey, + ) + ids <- teleporterMessageID +} + func SendBasicTeleporterMessage( ctx context.Context, source interfaces.SubnetTestInfo, diff --git a/types/types.go b/types/types.go index f9df9ffd..3393f5ed 100644 --- a/types/types.go +++ b/types/types.go @@ -8,6 +8,8 @@ import ( "errors" "math/big" + avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/awm-relayer/messages" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/ethclient" "github.com/ava-labs/subnet-evm/interfaces" @@ -23,16 +25,17 @@ var ErrInvalidLog = errors.New("invalid warp message log") // listener to process type WarpBlockInfo struct { BlockNumber uint64 - WarpLogs []types.Log + Messages []*WarpMessageInfo } -// WarpLogInfo describes the transaction information for the Warp message +// NewWarpMessageInfo describes the transaction information for the Warp message // sent on the source chain, and includes the Warp Message payload bytes -// WarpLogInfo instances are either derived from the logs of a block or +// NewWarpMessageInfo instances are either derived from the logs of a block or // from the manual Warp message information provided via configuration -type WarpLogInfo struct { - SourceAddress common.Address - UnsignedMsgBytes []byte +type WarpMessageInfo struct { + SourceAddress common.Address + UnsignedMessage *avalancheWarp.UnsignedMessage + MessageManager messages.MessageManager } // Extract Warp logs from the block, if they exist @@ -53,24 +56,50 @@ func NewWarpBlockInfo(header *types.Header, ethClient ethclient.Client) (*WarpBl return nil, err } } + messages := make([]*WarpMessageInfo, len(logs)) + for i, log := range logs { + warpLog, err := NewWarpMessageInfo(log) + if err != nil { + return nil, err + } + messages[i] = warpLog + } + return &WarpBlockInfo{ BlockNumber: header.Number.Uint64(), - WarpLogs: logs, + Messages: messages, }, nil } // Extract the Warp message information from the raw log -func NewWarpLogInfo(log types.Log) (*WarpLogInfo, error) { +func NewWarpMessageInfo(log types.Log) (*WarpMessageInfo, error) { if len(log.Topics) != 3 { return nil, ErrInvalidLog } if log.Topics[0] != WarpPrecompileLogFilter { return nil, ErrInvalidLog } + unsignedMsgBytes, err := UnpackWarpMessage(log.Data) + if err != nil { + return nil, err + } - return &WarpLogInfo{ - // BytesToAddress takes the last 20 bytes of the byte array if it is longer than 20 bytes - SourceAddress: common.BytesToAddress(log.Topics[1][:]), - UnsignedMsgBytes: log.Data, + return &WarpMessageInfo{ + SourceAddress: common.BytesToAddress(log.Topics[1][:]), + UnsignedMessage: unsignedMsgBytes, }, nil } + +func UnpackWarpMessage(unsignedMsgBytes []byte) (*avalancheWarp.UnsignedMessage, error) { + unsignedMsg, err := warp.UnpackSendWarpEventDataToMessage(unsignedMsgBytes) + if err != nil { + // If we failed to parse the message as a log, attempt to parse it as a standalone message + var standaloneErr error + unsignedMsg, standaloneErr = avalancheWarp.ParseUnsignedMessage(unsignedMsgBytes) + if standaloneErr != nil { + err = errors.Join(err, standaloneErr) + return nil, err + } + } + return unsignedMsg, nil +} From efa8f3b4afd53bbd90e4f64fc6715ff6867ac3b2 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Fri, 3 May 2024 18:50:36 +0000 Subject: [PATCH 12/49] restore integ tests --- tests/e2e_test.go | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 4d06011f..b5c6ffa1 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -62,22 +62,19 @@ var _ = ginkgo.AfterSuite(func() { }) var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { - // ginkgo.It("Manually Provided Message", func() { - // ManualMessage(localNetworkInstance) - // }) - // ginkgo.It("Basic Relay", func() { - // BasicRelay(localNetworkInstance) - // }) - // ginkgo.It("Teleporter Registry", func() { - // TeleporterRegistry(localNetworkInstance) - // }) - // ginkgo.It("Shared Database", func() { - // SharedDatabaseAccess(localNetworkInstance) - // }) - // ginkgo.It("Allowed Addresses", func() { - // AllowedAddresses(localNetworkInstance) - // }) - ginkgo.It("Queue Messages", func() { - QueueMessages(localNetworkInstance) + ginkgo.It("Manually Provided Message", func() { + ManualMessage(localNetworkInstance) + }) + ginkgo.It("Basic Relay", func() { + BasicRelay(localNetworkInstance) + }) + ginkgo.It("Teleporter Registry", func() { + TeleporterRegistry(localNetworkInstance) + }) + ginkgo.It("Shared Database", func() { + SharedDatabaseAccess(localNetworkInstance) + }) + ginkgo.It("Allowed Addresses", func() { + AllowedAddresses(localNetworkInstance) }) }) From de411b93e1f644f664b26eeb37b01b9be6c4ee4c Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Fri, 3 May 2024 14:29:51 +0000 Subject: [PATCH 13/49] combine prepare and process height --- main/main.go | 4 ++-- relayer/application_relayer.go | 23 ++++++++++------------- relayer/listener.go | 18 ++++++------------ 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/main/main.go b/main/main.go index b74fc422..2ed02cb3 100644 --- a/main/main.go +++ b/main/main.go @@ -347,7 +347,7 @@ func runRelayer( zap.String("blockchainID", sourceBlockchain.BlockchainID), zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMsgBytes)), ) - appRelayer, err := listener.RegisterMessageWithAppRelayer(warpMessage) + appRelayer, err := listener.RegisterMessageWithAppRelayer(0, warpMessage) if err != nil { logger.Error( "Failed to parse manual Warp message. Continuing.", @@ -356,7 +356,7 @@ func runRelayer( ) continue } - appRelayer.ProcessHeight(0) + appRelayer.ProcessHeight(0, false) } logger.Info( diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 209e65e0..3b05e38c 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -128,6 +128,7 @@ func NewApplicationRelayer( return &ar, nil } +// Registers a message to be relayed at a specific height by adding it to the pending messages list at that height. func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, unsignedMessage *avalancheWarp.UnsignedMessage, messageManager messages.MessageManager) { r.lock.Lock() defer r.lock.Unlock() @@ -142,19 +143,10 @@ func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, unsignedMess }) } -func (r *ApplicationRelayer) PrepareHeight(height uint64) { - r.lock.RLock() - defer r.lock.RUnlock() - r.logger.Debug( - "Preparing height to relay", - zap.Uint64("height", height), - zap.Int("numMessages", len(r.pendingMessages[height])), - zap.String("relayerID", r.relayerID.ID.String()), - ) - r.checkpointManager.PrepareHeight(height, uint64(len(r.pendingMessages[height]))) -} - -func (r *ApplicationRelayer) ProcessHeight(height uint64) { +// Processes all pending messages at a specific height. +// If checkpoint is true, Ppepares the number of messages to be relayed at a specific height with the checkpoint manager. +// This effectively marks the height as eligible for writing to the database once all messages are relayed. +func (r *ApplicationRelayer) ProcessHeight(height uint64, checkpoint bool) { r.lock.Lock() defer r.lock.Unlock() r.logger.Debug( @@ -163,7 +155,11 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64) { zap.Int("numMessages", len(r.pendingMessages[height])), zap.String("relayerID", r.relayerID.ID.String()), ) + if checkpoint { + r.checkpointManager.PrepareHeight(height, uint64(len(r.pendingMessages[height]))) + } for _, pendingMessage := range r.pendingMessages[height] { + // TODONOW: if relaying fails, we can keep the pending message in the list, and retry later go r.relayMessage( pendingMessage.unsignedMessage, r.currentRequestID, @@ -173,6 +169,7 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64) { ) r.currentRequestID++ } + delete(r.pendingMessages, height) } // TODONOW: this should write an error to a channel, which the caller should handle diff --git a/relayer/listener.go b/relayer/listener.go index 6b93f0fa..69e038a4 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -201,13 +201,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { zap.Uint64("blockNumber", block.BlockNumber), ) - // Iterate over the Warp logs in two passes: - // The first pass extracts the information needed to relay from the log, but does not initiate relaying - // This is so that the number of messages to be processed can be registered with the database before - // any messages are processed. - // The second pass dispatches the messages to the application relayers for processing - // expectedMessages := make(map[database.RelayerID]uint64) - // var msgsInfo []*parsedMessageInfo + // Register each message in the block with the appropriate application relayer for _, warpLog := range block.WarpLogs { warpLogInfo, err := relayerTypes.NewWarpLogInfo(warpLog) if err != nil { @@ -217,7 +211,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) continue } - _, err = lstnr.RegisterMessageWithAppRelayer(warpLogInfo) + _, err = lstnr.RegisterMessageWithAppRelayer(block.BlockNumber, warpLogInfo) if err != nil { lstnr.logger.Error( "Failed to parse message", @@ -227,9 +221,9 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { continue } } + // Initiate message relay of all registered messages for _, appRelayer := range lstnr.applicationRelayers { - appRelayer.PrepareHeight(block.BlockNumber) - appRelayer.ProcessHeight(block.BlockNumber) + appRelayer.ProcessHeight(block.BlockNumber, true) } case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) @@ -339,7 +333,7 @@ func (lstnr *Listener) getApplicationRelayer( return nil } -func (lstnr *Listener) RegisterMessageWithAppRelayer(warpLogInfo *relayerTypes.WarpLogInfo) ( +func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpLogInfo *relayerTypes.WarpLogInfo) ( *ApplicationRelayer, error, ) { @@ -393,6 +387,6 @@ func (lstnr *Listener) RegisterMessageWithAppRelayer(warpLogInfo *relayerTypes.W if appRelayer == nil { return nil, nil } - appRelayer.RegisterMessageAtHeight(0, unsignedMessage, messageManager) + appRelayer.RegisterMessageAtHeight(height, unsignedMessage, messageManager) return appRelayer, nil } From 9f79ca19923710f7b2b57f675063325dde5933c0 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Fri, 3 May 2024 19:37:58 +0000 Subject: [PATCH 14/49] refactor app relayer interface --- main/main.go | 4 +- relayer/application_relayer.go | 99 ++++++++++--------------- relayer/checkpoint/checkpoint.go | 121 ++++--------------------------- relayer/listener.go | 25 ++++--- 4 files changed, 72 insertions(+), 177 deletions(-) diff --git a/main/main.go b/main/main.go index 7d25465d..84039b1f 100644 --- a/main/main.go +++ b/main/main.go @@ -354,7 +354,7 @@ func runRelayer( zap.String("blockchainID", sourceBlockchain.BlockchainID), zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), ) - appRelayer, err := listener.RegisterMessageWithAppRelayer(0, warpMessage) + appRelayer, handler, err := listener.GetAppRelayerMessageHandler(0, warpMessage) if err != nil { logger.Error( "Failed to parse manual Warp message. Continuing.", @@ -363,7 +363,7 @@ func runRelayer( ) continue } - appRelayer.ProcessHeight(0, false) + appRelayer.ProcessMessage(handler) } logger.Info( diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 2fa1953d..b30b86e6 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -30,6 +30,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" + "golang.org/x/sync/errgroup" "go.uber.org/zap" ) @@ -68,7 +69,6 @@ type ApplicationRelayer struct { checkpointManager *checkpoint.CheckpointManager currentRequestID uint32 lock *sync.RWMutex - pendingMessages map[uint64][]messages.MessageHandler } func NewApplicationRelayer( @@ -118,80 +118,62 @@ func NewApplicationRelayer( checkpointManager: checkpointManager, currentRequestID: rand.Uint32(), // TODONOW: pass via ctor lock: &sync.RWMutex{}, - pendingMessages: make(map[uint64][]messages.MessageHandler), } return &ar, nil } -// // Processes all messages in the provided block info. -// // Checkpoints the height with the checkpoint manager when all messages are relayed. -// // It is up to the caller to populate [block] with messages intended to be processed by this application relayer. -// func (r *ApplicationRelayer) ProcessBlock(block relayerTypes.WarpBlockInfo) error { -// r.checkpointManager.PrepareHeight(block.BlockNumber, uint64(len(block.Messages))) -// for _, message := range block.Messages { -// r.ProcessMessage(message) -// } -// } - -// // Relays a message to the destination chain. Does not checkpoint the height. -// func (r *ApplicationRelayer) ProcessMessage(message *relayerTypes.WarpMessageInfo, height uint64) error { -// r.lock.Lock() -// defer r.lock.Unlock() -// go r.relayMessage( -// message.UnsignedMessage, -// r.currentRequestID, -// pendingMessage.messageManager, -// height, -// true, -// ) -// } - -// Registers a message to be relayed at a specific height by adding it to the pending messages list at that height. -func (r *ApplicationRelayer) RegisterMessageAtHeight(height uint64, messageHandler messages.MessageHandler) { - r.lock.Lock() - defer r.lock.Unlock() +// 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. +func (r *ApplicationRelayer) ProcessHeight(height uint64, msgs []messages.MessageHandler) error { + var eg errgroup.Group + for _, msg := range msgs { + eg.Go(func() error { + return r.ProcessMessage(msg) + }) + } + if err := eg.Wait(); err != nil { + r.logger.Error( + "Failed to process block", + zap.Uint64("height", height), + zap.String("relayerID", r.relayerID.ID.String()), + zap.Error(err), + ) + return err + } + r.checkpointManager.StageCommittedHeight(height) + r.logger.Debug( - "Registering message for relay", + "Processed block", zap.Uint64("height", height), zap.String("relayerID", r.relayerID.ID.String()), ) - r.pendingMessages[height] = append(r.pendingMessages[height], messageHandler) + return nil } -// Processes all pending messages at a specific height. -// If checkpoint is true, Prepares the number of messages to be relayed at a specific height with the checkpoint manager. -// This effectively marks the height as eligible for writing to the database once all messages are relayed. -func (r *ApplicationRelayer) ProcessHeight(height uint64, checkpoint bool) { +// Relays a message to the destination chain. Does not checkpoint the height. +func (r *ApplicationRelayer) ProcessMessage(msg messages.MessageHandler) error { + // Increment the request ID. Make sure we don't hold the lock while we relay the message. r.lock.Lock() - defer r.lock.Unlock() - r.logger.Debug( - "Processing height", - zap.Uint64("height", height), - zap.Int("numMessages", len(r.pendingMessages[height])), - zap.String("relayerID", r.relayerID.ID.String()), + r.currentRequestID++ + r.lock.Unlock() + + err := r.relayMessage( + r.currentRequestID, + msg, + true, ) - if checkpoint { - r.checkpointManager.PrepareHeight(height, uint64(len(r.pendingMessages[height]))) - } - for _, messageHandler := range r.pendingMessages[height] { - // TODONOW: if relaying fails, we can keep the pending message in the list, and retry later - go r.relayMessage( - r.currentRequestID, - messageHandler, - height, - true, - ) - r.currentRequestID++ - } - delete(r.pendingMessages, height) + + return err +} + +func (r *ApplicationRelayer) RelayerID() database.RelayerID { + return r.relayerID } -// TODONOW: this should write an error to a channel, which the caller should handle func (r *ApplicationRelayer) relayMessage( requestID uint32, messageHandler messages.MessageHandler, - blockNumber uint64, useAppRequestNetwork bool, ) error { shouldSend, err := messageHandler.ShouldSendMessage(r.relayerID.DestinationBlockchainID) @@ -205,7 +187,6 @@ func (r *ApplicationRelayer) relayMessage( } if !shouldSend { r.logger.Info("Message should not be sent") - r.checkpointManager.Finished(blockNumber) return nil } unsignedMessage := messageHandler.GetUnsignedMessage() @@ -253,8 +234,6 @@ func (r *ApplicationRelayer) relayMessage( ) r.incSuccessfulRelayMessageCount() - // Update the database with the latest processed block height - r.checkpointManager.Finished(blockNumber) return nil } diff --git a/relayer/checkpoint/checkpoint.go b/relayer/checkpoint/checkpoint.go index d69cec0e..c57b806e 100644 --- a/relayer/checkpoint/checkpoint.go +++ b/relayer/checkpoint/checkpoint.go @@ -19,15 +19,13 @@ import ( // type CheckpointManager struct { - logger logging.Logger - database database.RelayerDatabase - writeSignal chan struct{} - relayerID database.RelayerID - queuedHeightsAndMessages map[uint64]*messageCounter - committedHeight uint64 - lock *sync.RWMutex - pendingCommits *utils.UInt64Heap - finished chan uint64 + logger logging.Logger + database database.RelayerDatabase + writeSignal chan struct{} + relayerID database.RelayerID + committedHeight uint64 + lock *sync.RWMutex + pendingCommits *utils.UInt64Heap } func NewCheckpointManager( @@ -40,27 +38,20 @@ func NewCheckpointManager( h := &utils.UInt64Heap{} heap.Init(h) return &CheckpointManager{ - logger: logger, - database: database, - writeSignal: writeSignal, - relayerID: relayerID, - queuedHeightsAndMessages: make(map[uint64]*messageCounter), - committedHeight: startingHeight, - lock: &sync.RWMutex{}, - pendingCommits: h, - finished: make(chan uint64), + logger: logger, + database: database, + writeSignal: writeSignal, + relayerID: relayerID, + committedHeight: startingHeight, + lock: &sync.RWMutex{}, + pendingCommits: h, } } func (cm *CheckpointManager) Run() { - go cm.listenForFinishedRelays() go cm.listenForWriteSignal() } -func (cm *CheckpointManager) Finished(blockNumber uint64) { - cm.finished <- blockNumber -} - func (cm *CheckpointManager) writeToDatabase() { cm.lock.RLock() defer cm.lock.RUnlock() @@ -102,51 +93,12 @@ func (cm *CheckpointManager) listenForWriteSignal() { } } -func (cm *CheckpointManager) incrementFinishedCounter(height uint64) { - cm.lock.Lock() - defer cm.lock.Unlock() - counter, ok := cm.queuedHeightsAndMessages[height] - if !ok { - // This is expected for Warp messages that are not associated with the height greater than the latest processed block height. - // For example, on startup it is possible for an Application Relayer to re-process messages for a height that has already been committed. - // This is also the case for manual Warp messages that are processed out-of-band. - cm.logger.Debug( - "Pending height not found", - zap.Uint64("height", height), - zap.String("relayerID", cm.relayerID.ID.String()), - ) - return - } - - counter.processedMessages++ - cm.logger.Debug( - "Received finished signal", - zap.Uint64("height", height), - zap.String("relayerID", cm.relayerID.ID.String()), - zap.Uint64("processedMessages", counter.processedMessages), - zap.Uint64("totalMessages", counter.totalMessages), - ) - if counter.processedMessages == counter.totalMessages { - cm.stageCommittedHeight(height) - delete(cm.queuedHeightsAndMessages, height) - } -} - -// handleFinishedRelays listens for finished signals from the application relayer, and commits the -// height once all messages have been processed. -// This function should only be called once. -func (cm *CheckpointManager) listenForFinishedRelays() { - for height := range cm.finished { - cm.incrementFinishedCounter(height) - } -} - -// stageCommittedHeight queues a height to be written to the database. +// StageCommittedHeight queues a height to be written to the database. // Heights are committed in sequence, so if height is not exactly one // greater than the current committedHeight, it is instead cached in memory // to potentially be committed later. // Requires that cm.lock be held -func (cm *CheckpointManager) stageCommittedHeight(height uint64) { +func (cm *CheckpointManager) StageCommittedHeight(height uint64) { if height <= cm.committedHeight { cm.logger.Fatal( "Attempting to commit height less than or equal to the committed height", @@ -180,44 +132,3 @@ func (cm *CheckpointManager) stageCommittedHeight(height uint64) { } } } - -// PrepareHeight sets the total number of messages to be processed at a given height. -// Once all messages have been processed, the height is eligible to be committed. -// It is up to the caller to determine if a height is eligible to be committed. -// This function is thread safe. -func (cm *CheckpointManager) PrepareHeight(height uint64, totalMessages uint64) { - cm.lock.Lock() - defer cm.lock.Unlock() - // Heights less than or equal to the committed height are not candidates to write to the database. - // This is to ensure that writes are strictly increasing. - if height <= cm.committedHeight { - cm.logger.Debug( - "Skipping height", - zap.Uint64("height", height), - zap.Uint64("committedHeight", cm.committedHeight), - zap.String("relayerID", cm.relayerID.ID.String()), - ) - return - } - cm.logger.Debug( - "Preparing height", - zap.Uint64("height", height), - zap.Uint64("totalMessages", totalMessages), - zap.String("relayerID", cm.relayerID.ID.String()), - ) - // Short circuit to staging the height if there are no messages to process - if totalMessages == 0 { - cm.stageCommittedHeight(height) - return - } - cm.queuedHeightsAndMessages[height] = &messageCounter{ - totalMessages: totalMessages, - processedMessages: 0, - } -} - -// Helper type -type messageCounter struct { - totalMessages uint64 - processedMessages uint64 -} diff --git a/relayer/listener.go b/relayer/listener.go index 5ca3eaea..12a90bd1 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -227,8 +227,9 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) // Register each message in the block with the appropriate application relayer + messageHandlers := make(map[common.Hash][]messages.MessageHandler) for _, warpLogInfo := range block.Messages { - _, err = lstnr.RegisterMessageWithAppRelayer(block.BlockNumber, warpLogInfo) + appRelayer, handler, err := lstnr.GetAppRelayerMessageHandler(block.BlockNumber, warpLogInfo) if err != nil { lstnr.logger.Error( "Failed to parse message", @@ -237,10 +238,13 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) continue } + messageHandlers[appRelayer.relayerID.ID] = append(messageHandlers[appRelayer.relayerID.ID], handler) } - // Initiate message relay of all registered messages for _, appRelayer := range lstnr.applicationRelayers { - appRelayer.ProcessHeight(block.BlockNumber, true) + // 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] + appRelayer.ProcessHeight(block.BlockNumber, handlers) } case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) @@ -349,8 +353,10 @@ func (lstnr *Listener) getApplicationRelayer( return nil } -func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpMessageInfo *relayerTypes.WarpMessageInfo) ( +// TODONOW: Does this function make sense? There's probably a better way to organize this logic. +func (lstnr *Listener) GetAppRelayerMessageHandler(height uint64, warpMessageInfo *relayerTypes.WarpMessageInfo) ( *ApplicationRelayer, + messages.MessageHandler, error, ) { // Check that the warp message is from a supported message protocol contract address. @@ -362,7 +368,7 @@ func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpMessageI "Warp message from unsupported message protocol address. Not relaying.", zap.String("protocolAddress", warpMessageInfo.SourceAddress.Hex()), ) - return nil, nil + return nil, nil, nil } messageHandler, err := messageManager.NewMessageHandler(warpMessageInfo.UnsignedMessage) if err != nil { @@ -370,7 +376,7 @@ func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpMessageI "Failed to create message handler", zap.Error(err), ) - return nil, err + return nil, nil, err } // Fetch the message delivery data @@ -380,7 +386,7 @@ func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpMessageI "Failed to get message routing information", zap.Error(err), ) - return nil, err + return nil, nil, err } lstnr.logger.Info( @@ -399,8 +405,7 @@ func (lstnr *Listener) RegisterMessageWithAppRelayer(height uint64, warpMessageI destinationAddress, ) if appRelayer == nil { - return nil, nil + return nil, nil, nil } - appRelayer.RegisterMessageAtHeight(height, messageHandler) - return appRelayer, nil + return appRelayer, messageHandler, nil } From 7edff1de8acca04723ee7f7066bfcfa40f0a9d9d Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Fri, 10 May 2024 16:16:12 -0500 Subject: [PATCH 15/49] noop if committed height is unchanged --- relayer/checkpoint/checkpoint.go | 11 ++++++++--- relayer/listener.go | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/relayer/checkpoint/checkpoint.go b/relayer/checkpoint/checkpoint.go index c57b806e..a5c7f2bb 100644 --- a/relayer/checkpoint/checkpoint.go +++ b/relayer/checkpoint/checkpoint.go @@ -37,6 +37,11 @@ func NewCheckpointManager( ) *CheckpointManager { h := &utils.UInt64Heap{} heap.Init(h) + logger.Info( + "Creating checkpoint manager", + zap.String("relayerID", relayerID.ID.String()), + zap.Uint64("startingHeight", startingHeight), + ) return &CheckpointManager{ logger: logger, database: database, @@ -100,13 +105,13 @@ func (cm *CheckpointManager) listenForWriteSignal() { // Requires that cm.lock be held func (cm *CheckpointManager) StageCommittedHeight(height uint64) { if height <= cm.committedHeight { - cm.logger.Fatal( - "Attempting to commit height less than or equal to the committed height", + cm.logger.Debug( + "Attempting to commit height less than or equal to the committed height. Skipping.", zap.Uint64("height", height), zap.Uint64("committedHeight", cm.committedHeight), zap.String("relayerID", cm.relayerID.ID.String()), ) - panic("attempting to commit height less than or equal to the committed height") + return } // First push the height onto the pending commits min heap diff --git a/relayer/listener.go b/relayer/listener.go index 12a90bd1..a68f2261 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -238,6 +238,10 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { ) 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) } for _, appRelayer := range lstnr.applicationRelayers { From 2ee56bbe962b10735735146d2144c939f95a78f3 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Fri, 10 May 2024 16:27:11 -0500 Subject: [PATCH 16/49] remove unused var --- messages/teleporter/message_manager.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/messages/teleporter/message_manager.go b/messages/teleporter/message_manager.go index a0e9b4e2..df7c3113 100644 --- a/messages/teleporter/message_manager.go +++ b/messages/teleporter/message_manager.go @@ -23,10 +23,6 @@ import ( "go.uber.org/zap" ) -const ( - teleporterMessageCacheSize = 100 -) - type messageManager struct { messageConfig Config protocolAddress common.Address From 72f6f0cc4fbbded291ace7890a1deacfaf51d55c Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Fri, 10 May 2024 16:27:19 -0500 Subject: [PATCH 17/49] fix unit tests --- .../message_manager_test.go | 5 +++-- messages/teleporter/message_manager_test.go | 22 +++++++++++-------- relayer/checkpoint/checkpoint_test.go | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/messages/off-chain-registry/message_manager_test.go b/messages/off-chain-registry/message_manager_test.go index c9c46768..3561734f 100644 --- a/messages/off-chain-registry/message_manager_test.go +++ b/messages/off-chain-registry/message_manager_test.go @@ -160,8 +160,9 @@ func TestShouldSendMessage(t *testing.T) { } else { unsignedMessage = createRegistryUnsignedWarpMessage(t, test.entry, teleporterRegistryAddress, test.destinationBlockchainID) } - - result, err := messageManager.ShouldSendMessage(unsignedMessage, test.destinationBlockchainID) + messageHandler, err := messageManager.NewMessageHandler(unsignedMessage) + require.NoError(t, err) + result, err := messageHandler.ShouldSendMessage(test.destinationBlockchainID) if test.expectedError { require.Error(t, err) } else { diff --git a/messages/teleporter/message_manager_test.go b/messages/teleporter/message_manager_test.go index 8c95b4bc..d226f1b7 100644 --- a/messages/teleporter/message_manager_test.go +++ b/messages/teleporter/message_manager_test.go @@ -106,7 +106,7 @@ func TestShouldSendMessage(t *testing.T) { senderAddressTimes int clientTimes int messageReceivedCall *CallContractChecker - expectedError bool + expectedParseError bool expectedResult bool }{ { @@ -127,7 +127,7 @@ func TestShouldSendMessage(t *testing.T) { name: "invalid message", destinationBlockchainID: destinationBlockchainID, warpUnsignedMessage: invalidWarpUnsignedMessage, - expectedError: true, + expectedParseError: true, }, { name: "invalid destination chain id", @@ -177,6 +177,14 @@ func TestShouldSendMessage(t *testing.T) { destinationClients, ) require.NoError(t, err) + messageHandler, err := messageManager.NewMessageHandler(test.warpUnsignedMessage) + if test.expectedParseError { + // If we expect an error parsing the Warp message, we should not call ShouldSendMessage + require.Error(t, err) + return + } else { + require.NoError(t, err) + } ethClient := mock_evm.NewMockClient(ctrl) mockClient.EXPECT(). Client(). @@ -198,13 +206,9 @@ func TestShouldSendMessage(t *testing.T) { Times(test.messageReceivedCall.times) } - result, err := messageManager.ShouldSendMessage(test.warpUnsignedMessage, test.destinationBlockchainID) - if test.expectedError { - require.Error(t, err) - } else { - require.NoError(t, err) - require.Equal(t, test.expectedResult, result) - } + result, err := messageHandler.ShouldSendMessage(test.destinationBlockchainID) + require.NoError(t, err) + require.Equal(t, test.expectedResult, result) }) } } diff --git a/relayer/checkpoint/checkpoint_test.go b/relayer/checkpoint/checkpoint_test.go index 5c7aac1f..155616a9 100644 --- a/relayer/checkpoint/checkpoint_test.go +++ b/relayer/checkpoint/checkpoint_test.go @@ -67,7 +67,7 @@ func TestCommitHeight(t *testing.T) { heap.Init(test.pendingHeights) cm.pendingCommits = test.pendingHeights cm.committedHeight = test.currentMaxHeight - cm.stageCommittedHeight(test.commitHeight) + cm.StageCommittedHeight(test.commitHeight) require.Equal(t, test.expectedMaxHeight, cm.committedHeight, test.name) } } From 90391a6c33c3aef50c373e140f92a3c2b7d5ad5d Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Mon, 13 May 2024 10:38:09 -0500 Subject: [PATCH 18/49] go 1.22.3 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a2dd3e34..43dfe0f5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ava-labs/awm-relayer -go 1.21.10 +go 1.22.3 require ( github.com/ava-labs/avalanche-network-runner v1.7.6 From 69dbc80373a46ad84cea153fc1433751acbbf628 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Mon, 13 May 2024 10:41:59 -0500 Subject: [PATCH 19/49] bump golangci-lint version --- scripts/versions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/versions.sh b/scripts/versions.sh index 3cd73919..292e87f3 100755 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -22,4 +22,4 @@ GINKGO_VERSION=${GINKGO_VERSION:-$(getDepVersion github.com/onsi/ginkgo/v2)} SUBNET_EVM_VERSION=${SUBNET_EVM_VERSION:-$(getDepVersion github.com/ava-labs/subnet-evm)} # Set golangci-lint version -GOLANGCI_LINT_VERSION=${GOLANGCI_LINT_VERSION:-'v1.55'} +GOLANGCI_LINT_VERSION=${GOLANGCI_LINT_VERSION:-'v1.58.1'} From b57d05ded780541861605c2764ef6cfdb180df3d Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 14:35:05 -0500 Subject: [PATCH 20/49] add batch Teleporter message test --- .gitignore | 4 + .gitmodules | 6 + README.md | 4 + relayer/application_relayer.go | 1 + scripts/abi_bindings.sh | 84 + scripts/constants.sh | 3 + .../BatchCrossChainMessenger.go | 1411 +++++++++++++++++ tests/batch_relay.go | 110 ++ tests/contracts/foundry.toml | 26 + tests/contracts/lib/forge-std | 1 + tests/contracts/lib/teleporter | 1 + tests/contracts/remappings.txt | 3 + .../src/BatchCrossChainMessenger.sol | 129 ++ tests/e2e_test.go | 3 + tests/utils/utils.go | 24 + 15 files changed, 1810 insertions(+) create mode 100644 .gitmodules create mode 100755 scripts/abi_bindings.sh create mode 100644 tests/abi-bindings/go/BatchCrossChainMessenger/BatchCrossChainMessenger.go create mode 100644 tests/batch_relay.go create mode 100644 tests/contracts/foundry.toml create mode 160000 tests/contracts/lib/forge-std create mode 160000 tests/contracts/lib/teleporter create mode 100644 tests/contracts/remappings.txt create mode 100644 tests/contracts/src/BatchCrossChainMessenger.sol diff --git a/.gitignore b/.gitignore index b6bef6d8..003022bb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ relayer-config.json main.log server.log *.test + +# Foundry outputs +cache/ +out/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..15d57c4e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "tests/contracts/lib/teleporter"] + path = tests/contracts/lib/teleporter + url = https://github.com/ava-labs/teleporter +[submodule "tests/contracts/lib/forge-std"] + path = tests/contracts/lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/README.md b/README.md index 4fd0ce1c..229e0f4f 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,10 @@ The Fuji and Mainnet [public API nodes](https://docs.avax.network/tooling/rpc-pr ## Usage +### Initialize the repository + +- Get all submodules: `git submodule update --init --recursive` + ### Building Before building, be sure to install Go, which is required even if you're just building the Docker image. diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 0ac9285b..50dfcddd 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -146,6 +146,7 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, msgs []messages.Messag r.logger.Debug( "Processed block", zap.Uint64("height", height), + zap.String("sourceBlockchainID", r.relayerID.SourceBlockchainID.String()), zap.String("relayerID", r.relayerID.ID.String()), ) return nil diff --git a/scripts/abi_bindings.sh b/scripts/abi_bindings.sh new file mode 100755 index 00000000..2e91d3e1 --- /dev/null +++ b/scripts/abi_bindings.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +# See the file LICENSE for licensing terms. + +set -e + +AWM_RELAYER_PATH=$( + cd "$(dirname "${BASH_SOURCE[0]}")" + cd .. && pwd +) + +source $AWM_RELAYER_PATH/scripts/constants.sh +source $AWM_RELAYER_PATH/scripts/versions.sh +source $TELEPORTER_PATH/scripts/utils.sh + +setARCH + +# Contract names to generate Go bindings for +DEFAULT_CONTRACT_LIST="BatchCrossChainMessenger" + +CONTRACT_LIST= +HELP= +while [ $# -gt 0 ]; do + case "$1" in + -c | --contract) CONTRACT_LIST=$2 ;; + -h | --help) HELP=true ;; + esac + shift +done + +if [ "$HELP" = true ]; then + echo "Usage: ./scripts/abi_bindings.sh [OPTIONS]" + echo "Build contracts and generate Go bindings" + echo "" + echo "Options:" + echo " -c, --contract Generate Go bindings for the contract. If empty, generate Go bindings for a default list of contracts" + echo " -c, --contract "contract1 contract2" Generate Go bindings for multiple contracts" + echo " -h, --help Print this help message" + exit 0 +fi + +if ! command -v forge &> /dev/null; then + echo "forge not found." && exit 1 +fi + +echo "Building subnet-evm abigen" +go install github.com/ava-labs/subnet-evm/cmd/abigen@${SUBNET_EVM_VERSION} + +# Force recompile of all contracts to prevent against using previous +# compilations that did not generate new ABI files. +echo "Building Contracts" +cd $AWM_RELAYER_PATH/tests/contracts +forge build --force --extra-output-files abi bin + +contract_names=($CONTRACT_LIST) + +# If CONTRACT_LIST is empty, use DEFAULT_CONTRACT_LIST +if [[ -z "${CONTRACT_LIST}" ]]; then + contract_names=($DEFAULT_CONTRACT_LIST) +fi + +cd $AWM_RELAYER_PATH/tests/contracts/src +for contract_name in "${contract_names[@]}" +do + path=$(find . -name $contract_name.sol) + dir=$(dirname $path) + abi_file=$AWM_RELAYER_PATH/tests/contracts/out/$contract_name.sol/$contract_name.abi.json + if ! [ -f $abi_file ]; then + echo "Error: Contract $contract_name abi file not found" + exit 1 + fi + + echo "Generating Go bindings for $contract_name..." + gen_path=$AWM_RELAYER_PATH/tests/abi-bindings/go/$dir/$contract_name + mkdir -p $gen_path + $GOPATH/bin/abigen --abi $abi_file \ + --pkg $(convertToLower $contract_name) \ + --bin $AWM_RELAYER_PATH/tests/contracts/out/$contract_name.sol/$contract_name.bin \ + --type $contract_name \ + --out $gen_path/$contract_name.go + echo "Done generating Go bindings for $contract_name." +done + +exit 0 diff --git a/scripts/constants.sh b/scripts/constants.sh index bfb90688..adf237a1 100755 --- a/scripts/constants.sh +++ b/scripts/constants.sh @@ -16,6 +16,9 @@ relayer_path="$RELAYER_PATH/build/awm-relayer" # Set the PATHS GOPATH="$(go env GOPATH)" +TELEPORTER_PATH="$RELAYER_PATH"/tests/contracts/lib/teleporter +source $TELEPORTER_PATH/scripts/constants.sh + # Avalabs docker hub repo is avaplatform/awm-relayer. # Here we default to the local image (awm-relayer) as to avoid unintentional pushes # You should probably set it - export DOCKER_REPO='avaplatform/awm-relayer' diff --git a/tests/abi-bindings/go/BatchCrossChainMessenger/BatchCrossChainMessenger.go b/tests/abi-bindings/go/BatchCrossChainMessenger/BatchCrossChainMessenger.go new file mode 100644 index 00000000..d4a97ac2 --- /dev/null +++ b/tests/abi-bindings/go/BatchCrossChainMessenger/BatchCrossChainMessenger.go @@ -0,0 +1,1411 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package batchcrosschainmessenger + +import ( + "errors" + "math/big" + "strings" + + "github.com/ava-labs/subnet-evm/accounts/abi" + "github.com/ava-labs/subnet-evm/accounts/abi/bind" + "github.com/ava-labs/subnet-evm/core/types" + "github.com/ava-labs/subnet-evm/interfaces" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = interfaces.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// BatchCrossChainMessengerMetaData contains all meta data concerning the BatchCrossChainMessenger contract. +var BatchCrossChainMessengerMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterRegistryAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"teleporterManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"oldMinTeleporterVersion\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"newMinTeleporterVersion\",\"type\":\"uint256\"}],\"name\":\"MinTeleporterVersionUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"originSenderAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"messages\",\"type\":\"string[]\"}],\"name\":\"ReceiveMessages\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"destinationBlockchainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeTokenAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"messages\",\"type\":\"string[]\"}],\"name\":\"SendMessages\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"TeleporterAddressPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"TeleporterAddressUnpaused\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"}],\"name\":\"getCurrentMessages\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMinTeleporterVersion\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"isTeleporterAddressPaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"pauseTeleporterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"originSenderAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"receiveTeleporterMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationBlockchainID\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"string[]\",\"name\":\"messages\",\"type\":\"string[]\"}],\"name\":\"sendMessages\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"teleporterRegistry\",\"outputs\":[{\"internalType\":\"contractTeleporterRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"unpauseTeleporterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"version\",\"type\":\"uint256\"}],\"name\":\"updateMinTeleporterVersion\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b50604051620020403803806200204083398101604081905262000034916200029f565b60016000558181816001600160a01b038116620000be5760405162461bcd60e51b815260206004820152603760248201527f54656c65706f727465725570677261646561626c653a207a65726f2074656c6560448201527f706f72746572207265676973747279206164647265737300000000000000000060648201526084015b60405180910390fd5b6001600160a01b03811660808190526040805163301fd1f560e21b8152905163c07f47d4916004808201926020929091908290030181865afa15801562000109573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200012f9190620002d7565b600255506200013e3362000153565b6200014981620001a5565b50505050620002f1565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b620001af62000224565b6001600160a01b038116620002165760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401620000b5565b620002218162000153565b50565b6003546001600160a01b03163314620002805760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620000b5565b565b80516001600160a01b03811681146200029a57600080fd5b919050565b60008060408385031215620002b357600080fd5b620002be8362000282565b9150620002ce6020840162000282565b90509250929050565b600060208284031215620002ea57600080fd5b5051919050565b608051611d1f620003216000396000818160be0152818161073701528181610c570152610fce0152611d1f6000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c80638da5cb5b116100715780638da5cb5b146101605780639731429714610171578063c1329fcb146101ad578063c868efaa146101ce578063d2cc7a70146101e1578063f2fde38b146101f257600080fd5b80631a7f5bec146100b95780632b0d8f18146100fd5780633902970c146101125780634511243e146101325780635eb9951414610145578063715018a614610158575b600080fd5b6100e07f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61011061010b3660046114c8565b610205565b005b610125610120366004611578565b61030a565b6040516100f491906116c3565b6101106101403660046114c8565b6104e8565b610110610153366004611707565b6105e5565b6101106105f9565b6003546001600160a01b03166100e0565b61019d61017f3660046114c8565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100f4565b6101c06101bb366004611707565b61060d565b6040516100f49291906117c5565b6101106101dc3660046117e9565b610722565b6002546040519081526020016100f4565b6101106102003660046114c8565b6108ec565b61020d610962565b6001600160a01b03811661023c5760405162461bcd60e51b815260040161023390611872565b60405180910390fd5b6001600160a01b03811660009081526001602052604090205460ff16156102bb5760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a2061646472657373206160448201526c1b1c9958591e481c185d5cd959609a1b6064820152608401610233565b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f933f93e57a222e6330362af8b376d0a8725b6901e9a2fb86d00f169702b28a4c9190a250565b606061031461096a565b600084156103295761032686866109c3565b90505b866001600160a01b0316887f430d1906813fdb2129a19139f4112a1396804605501a798df3a4042590ba20d58884888860405161036994939291906118c0565b60405180910390a36000835167ffffffffffffffff81111561038d5761038d6114e5565b6040519080825280602002602001820160405280156103b6578160200160208202803683370190505b50905060005b84518110156104d057600061049d6040518060c001604052808d81526020018c6001600160a01b0316815260200160405180604001604052808d6001600160a01b03168152602001888152508152602001898152602001600067ffffffffffffffff81111561042d5761042d6114e5565b604051908082528060200260200182016040528015610456578160200160208202803683370190505b50815260200188858151811061046e5761046e6118ed565b60200260200101516040516020016104869190611903565b604051602081830303815290604052815250610b2d565b9050808383815181106104b2576104b26118ed565b602090810291909101015250806104c88161192c565b9150506103bc565b509150506104de6001600055565b9695505050505050565b6104f0610962565b6001600160a01b0381166105165760405162461bcd60e51b815260040161023390611872565b6001600160a01b03811660009081526001602052604090205460ff166105905760405162461bcd60e51b815260206004820152602960248201527f54656c65706f727465725570677261646561626c653a2061646472657373206e6044820152681bdd081c185d5cd95960ba1b6064820152608401610233565b6040516001600160a01b038216907f844e2f3154214672229235858fd029d1dfd543901c6d05931f0bc2480a2d72c390600090a26001600160a01b03166000908152600160205260409020805460ff19169055565b6105ed610962565b6105f681610c53565b50565b610601610df3565b61060b6000610e4d565b565b60008181526004602090815260408083208151808301835281546001600160a01b0316815260018201805484518187028101870190955280855260609587959394938582019390929091879084015b8282101561070857838290600052602060002001805461067b90611945565b80601f01602080910402602001604051908101604052809291908181526020018280546106a790611945565b80156106f45780601f106106c9576101008083540402835291602001916106f4565b820191906000526020600020905b8154815290600101906020018083116106d757829003601f168201915b50505050508152602001906001019061065c565b505050915250508051602090910151909590945092505050565b61072a61096a565b6002546001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016634c1f08ce336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156107a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c5919061197f565b101561082c5760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201526f32b632b837b93a32b91039b2b73232b960811b6064820152608401610233565b6108353361017f565b1561089b5760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881859191c995cdcc81c185d5cd95960821b6064820152608401610233565b6108dc848484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e9f92505050565b6108e66001600055565b50505050565b6108f4610df3565b6001600160a01b0381166109595760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610233565b6105f681610e4d565b61060b610df3565b6002600054036109bc5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610233565b6002600055565b6040516370a0823160e01b815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa158015610a0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a30919061197f565b9050610a476001600160a01b038516333086610f5e565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015610a8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab2919061197f565b9050818111610b185760405162461bcd60e51b815260206004820152602c60248201527f5361666545524332305472616e7366657246726f6d3a2062616c616e6365206e60448201526b1bdd081a5b98dc99585cd95960a21b6064820152608401610233565b610b228282611998565b925050505b92915050565b600080610b38610fc9565b60408401516020015190915015610bdd576040830151516001600160a01b0316610bba5760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a207a65726f206665652060448201526c746f6b656e206164647265737360981b6064820152608401610233565b604083015160208101519051610bdd916001600160a01b039091169083906110dd565b604051630624488560e41b81526001600160a01b03821690636244885090610c099086906004016119ef565b6020604051808303816000875af1158015610c28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4c919061197f565b9392505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cb3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd7919061197f565b60025490915081831115610d475760405162461bcd60e51b815260206004820152603160248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201527032b632b837b93a32b9103b32b939b4b7b760791b6064820152608401610233565b808311610dbc5760405162461bcd60e51b815260206004820152603f60248201527f54656c65706f727465725570677261646561626c653a206e6f7420677265617460448201527f6572207468616e2063757272656e74206d696e696d756d2076657273696f6e006064820152608401610233565b6002839055604051839082907fa9a7ef57e41f05b4c15480842f5f0c27edfcbb553fed281f7c4068452cc1c02d90600090a3505050565b6003546001600160a01b0316331461060b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610233565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600081806020019051810190610eb59190611a6d565b6040805180820182526001600160a01b038681168252602080830185815260008a81526004835294909420835181546001600160a01b03191693169290921782559251805194955091939092610f129260018501929101906113f6565b50905050826001600160a01b0316847f903a123a8d947dcbd005d05b20bb5180f878c6d3527681c1abb1d8987714cb9d83604051610f509190611b5a565b60405180910390a350505050565b6040516001600160a01b03808516602483015283166044820152606481018290526108e69085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261118f565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d820e64f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561102a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104e9190611b6d565b9050611072816001600160a01b031660009081526001602052604090205460ff1690565b156110d85760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881cd95b991a5b99c81c185d5cd95960821b6064820152608401610233565b919050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa15801561112e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611152919061197f565b61115c9190611b8a565b6040516001600160a01b0385166024820152604481018290529091506108e690859063095ea7b360e01b90606401610f92565b60006111e4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166112669092919063ffffffff16565b80519091501561126157808060200190518101906112029190611b9d565b6112615760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610233565b505050565b6060611275848460008561127d565b949350505050565b6060824710156112de5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610233565b600080866001600160a01b031685876040516112fa9190611bbf565b60006040518083038185875af1925050503d8060008114611337576040519150601f19603f3d011682016040523d82523d6000602084013e61133c565b606091505b509150915061134d87838387611358565b979650505050505050565b606083156113c75782516000036113c0576001600160a01b0385163b6113c05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610233565b5081611275565b61127583838151156113dc5781518083602001fd5b8060405162461bcd60e51b81526004016102339190611903565b82805482825590600052602060002090810192821561143c579160200282015b8281111561143c578251829061142c9082611c29565b5091602001919060010190611416565b5061144892915061144c565b5090565b808211156114485760006114608282611469565b5060010161144c565b50805461147590611945565b6000825580601f10611485575050565b601f0160209004906000526020600020908101906105f691905b80821115611448576000815560010161149f565b6001600160a01b03811681146105f657600080fd5b6000602082840312156114da57600080fd5b8135610c4c816114b3565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611524576115246114e5565b604052919050565b600067ffffffffffffffff821115611546576115466114e5565b5060051b60200190565b600067ffffffffffffffff82111561156a5761156a6114e5565b50601f01601f191660200190565b60008060008060008060c0878903121561159157600080fd5b863595506115a260208801356114b3565b602087013594506115b660408801356114b3565b60408701359350606087013592506080870135915067ffffffffffffffff60a088013511156115e457600080fd5b60a0870135870188601f8201126115fa57600080fd5b61160c611607823561152c565b6114fb565b81358082526020808301929160051b8401018b81111561162b57600080fd5b602084015b818110156116b15767ffffffffffffffff8135111561164e57600080fd5b803585018d603f82011261166157600080fd5b602081013561167261160782611550565b8181528f604083850101111561168757600080fd5b81604084016020830137600060208383010152808752505050602084019350602081019050611630565b50508093505050509295509295509295565b6020808252825182820181905260009190848201906040850190845b818110156116fb578351835292840192918401916001016116df565b50909695505050505050565b60006020828403121561171957600080fd5b5035919050565b60005b8381101561173b578181015183820152602001611723565b50506000910152565b6000815180845261175c816020860160208601611720565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b858110156117b85782840389526117a6848351611744565b9885019893509084019060010161178e565b5091979650505050505050565b6001600160a01b038316815260406020820181905260009061127590830184611770565b600080600080606085870312156117ff57600080fd5b843593506020850135611811816114b3565b9250604085013567ffffffffffffffff8082111561182e57600080fd5b818701915087601f83011261184257600080fd5b81358181111561185157600080fd5b88602082850101111561186357600080fd5b95989497505060200194505050565b6020808252602e908201527f54656c65706f727465725570677261646561626c653a207a65726f2054656c6560408201526d706f72746572206164647265737360901b606082015260800190565b60018060a01b03851681528360208201528260408201526080606082015260006104de6080830184611770565b634e487b7160e01b600052603260045260246000fd5b602081526000610c4c6020830184611744565b634e487b7160e01b600052601160045260246000fd5b60006001820161193e5761193e611916565b5060010190565b600181811c9082168061195957607f821691505b60208210810361197957634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561199157600080fd5b5051919050565b81810381811115610b2757610b27611916565b600081518084526020808501945080840160005b838110156119e45781516001600160a01b0316875295820195908201906001016119bf565b509495945050505050565b60208152815160208201526000602083015160018060a01b03808216604085015260408501519150808251166060850152506020810151608084015250606083015160a0830152608083015160e060c0840152611a506101008401826119ab565b905060a0840151601f198483030160e0850152610b228282611744565b60006020808385031215611a8057600080fd5b825167ffffffffffffffff80821115611a9857600080fd5b818501915085601f830112611aac57600080fd5b8151611aba6116078261152c565b81815260059190911b83018401908481019088831115611ad957600080fd5b8585015b83811015611b4d57805185811115611af55760008081fd5b8601603f81018b13611b075760008081fd5b878101516040611b1961160783611550565b8281528d82848601011115611b2e5760008081fd5b611b3d838c8301848701611720565b8652505050918601918601611add565b5098975050505050505050565b602081526000610c4c6020830184611770565b600060208284031215611b7f57600080fd5b8151610c4c816114b3565b80820180821115610b2757610b27611916565b600060208284031215611baf57600080fd5b81518015158114610c4c57600080fd5b60008251611bd1818460208701611720565b9190910192915050565b601f82111561126157600081815260208120601f850160051c81016020861015611c025750805b601f850160051c820191505b81811015611c2157828155600101611c0e565b505050505050565b815167ffffffffffffffff811115611c4357611c436114e5565b611c5781611c518454611945565b84611bdb565b602080601f831160018114611c8c5760008415611c745750858301515b600019600386901b1c1916600185901b178555611c21565b600085815260208120601f198616915b82811015611cbb57888601518255948401946001909101908401611c9c565b5085821015611cd95787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea264697066735822122099c1934ed6c29efc8a8f1a00000c54700422fb3a36d744d19eddd761d3af6f2c64736f6c63430008120033", +} + +// BatchCrossChainMessengerABI is the input ABI used to generate the binding from. +// Deprecated: Use BatchCrossChainMessengerMetaData.ABI instead. +var BatchCrossChainMessengerABI = BatchCrossChainMessengerMetaData.ABI + +// BatchCrossChainMessengerBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use BatchCrossChainMessengerMetaData.Bin instead. +var BatchCrossChainMessengerBin = BatchCrossChainMessengerMetaData.Bin + +// DeployBatchCrossChainMessenger deploys a new Ethereum contract, binding an instance of BatchCrossChainMessenger to it. +func DeployBatchCrossChainMessenger(auth *bind.TransactOpts, backend bind.ContractBackend, teleporterRegistryAddress common.Address, teleporterManager common.Address) (common.Address, *types.Transaction, *BatchCrossChainMessenger, error) { + parsed, err := BatchCrossChainMessengerMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(BatchCrossChainMessengerBin), backend, teleporterRegistryAddress, teleporterManager) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &BatchCrossChainMessenger{BatchCrossChainMessengerCaller: BatchCrossChainMessengerCaller{contract: contract}, BatchCrossChainMessengerTransactor: BatchCrossChainMessengerTransactor{contract: contract}, BatchCrossChainMessengerFilterer: BatchCrossChainMessengerFilterer{contract: contract}}, nil +} + +// BatchCrossChainMessenger is an auto generated Go binding around an Ethereum contract. +type BatchCrossChainMessenger struct { + BatchCrossChainMessengerCaller // Read-only binding to the contract + BatchCrossChainMessengerTransactor // Write-only binding to the contract + BatchCrossChainMessengerFilterer // Log filterer for contract events +} + +// BatchCrossChainMessengerCaller is an auto generated read-only Go binding around an Ethereum contract. +type BatchCrossChainMessengerCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BatchCrossChainMessengerTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BatchCrossChainMessengerTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BatchCrossChainMessengerFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BatchCrossChainMessengerFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BatchCrossChainMessengerSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BatchCrossChainMessengerSession struct { + Contract *BatchCrossChainMessenger // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BatchCrossChainMessengerCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BatchCrossChainMessengerCallerSession struct { + Contract *BatchCrossChainMessengerCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BatchCrossChainMessengerTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BatchCrossChainMessengerTransactorSession struct { + Contract *BatchCrossChainMessengerTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BatchCrossChainMessengerRaw is an auto generated low-level Go binding around an Ethereum contract. +type BatchCrossChainMessengerRaw struct { + Contract *BatchCrossChainMessenger // Generic contract binding to access the raw methods on +} + +// BatchCrossChainMessengerCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BatchCrossChainMessengerCallerRaw struct { + Contract *BatchCrossChainMessengerCaller // Generic read-only contract binding to access the raw methods on +} + +// BatchCrossChainMessengerTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BatchCrossChainMessengerTransactorRaw struct { + Contract *BatchCrossChainMessengerTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBatchCrossChainMessenger creates a new instance of BatchCrossChainMessenger, bound to a specific deployed contract. +func NewBatchCrossChainMessenger(address common.Address, backend bind.ContractBackend) (*BatchCrossChainMessenger, error) { + contract, err := bindBatchCrossChainMessenger(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &BatchCrossChainMessenger{BatchCrossChainMessengerCaller: BatchCrossChainMessengerCaller{contract: contract}, BatchCrossChainMessengerTransactor: BatchCrossChainMessengerTransactor{contract: contract}, BatchCrossChainMessengerFilterer: BatchCrossChainMessengerFilterer{contract: contract}}, nil +} + +// NewBatchCrossChainMessengerCaller creates a new read-only instance of BatchCrossChainMessenger, bound to a specific deployed contract. +func NewBatchCrossChainMessengerCaller(address common.Address, caller bind.ContractCaller) (*BatchCrossChainMessengerCaller, error) { + contract, err := bindBatchCrossChainMessenger(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerCaller{contract: contract}, nil +} + +// NewBatchCrossChainMessengerTransactor creates a new write-only instance of BatchCrossChainMessenger, bound to a specific deployed contract. +func NewBatchCrossChainMessengerTransactor(address common.Address, transactor bind.ContractTransactor) (*BatchCrossChainMessengerTransactor, error) { + contract, err := bindBatchCrossChainMessenger(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerTransactor{contract: contract}, nil +} + +// NewBatchCrossChainMessengerFilterer creates a new log filterer instance of BatchCrossChainMessenger, bound to a specific deployed contract. +func NewBatchCrossChainMessengerFilterer(address common.Address, filterer bind.ContractFilterer) (*BatchCrossChainMessengerFilterer, error) { + contract, err := bindBatchCrossChainMessenger(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerFilterer{contract: contract}, nil +} + +// bindBatchCrossChainMessenger binds a generic wrapper to an already deployed contract. +func bindBatchCrossChainMessenger(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := BatchCrossChainMessengerMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BatchCrossChainMessenger *BatchCrossChainMessengerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BatchCrossChainMessenger.Contract.BatchCrossChainMessengerCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BatchCrossChainMessenger *BatchCrossChainMessengerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.BatchCrossChainMessengerTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BatchCrossChainMessenger *BatchCrossChainMessengerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.BatchCrossChainMessengerTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BatchCrossChainMessenger.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.contract.Transact(opts, method, params...) +} + +// GetCurrentMessages is a free data retrieval call binding the contract method 0xc1329fcb. +// +// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(address, string[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCaller) GetCurrentMessages(opts *bind.CallOpts, sourceBlockchainID [32]byte) (common.Address, []string, error) { + var out []interface{} + err := _BatchCrossChainMessenger.contract.Call(opts, &out, "getCurrentMessages", sourceBlockchainID) + + if err != nil { + return *new(common.Address), *new([]string), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + out1 := *abi.ConvertType(out[1], new([]string)).(*[]string) + + return out0, out1, err + +} + +// GetCurrentMessages is a free data retrieval call binding the contract method 0xc1329fcb. +// +// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(address, string[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) GetCurrentMessages(sourceBlockchainID [32]byte) (common.Address, []string, error) { + return _BatchCrossChainMessenger.Contract.GetCurrentMessages(&_BatchCrossChainMessenger.CallOpts, sourceBlockchainID) +} + +// GetCurrentMessages is a free data retrieval call binding the contract method 0xc1329fcb. +// +// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(address, string[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCallerSession) GetCurrentMessages(sourceBlockchainID [32]byte) (common.Address, []string, error) { + return _BatchCrossChainMessenger.Contract.GetCurrentMessages(&_BatchCrossChainMessenger.CallOpts, sourceBlockchainID) +} + +// GetMinTeleporterVersion is a free data retrieval call binding the contract method 0xd2cc7a70. +// +// Solidity: function getMinTeleporterVersion() view returns(uint256) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCaller) GetMinTeleporterVersion(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _BatchCrossChainMessenger.contract.Call(opts, &out, "getMinTeleporterVersion") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetMinTeleporterVersion is a free data retrieval call binding the contract method 0xd2cc7a70. +// +// Solidity: function getMinTeleporterVersion() view returns(uint256) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) GetMinTeleporterVersion() (*big.Int, error) { + return _BatchCrossChainMessenger.Contract.GetMinTeleporterVersion(&_BatchCrossChainMessenger.CallOpts) +} + +// GetMinTeleporterVersion is a free data retrieval call binding the contract method 0xd2cc7a70. +// +// Solidity: function getMinTeleporterVersion() view returns(uint256) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCallerSession) GetMinTeleporterVersion() (*big.Int, error) { + return _BatchCrossChainMessenger.Contract.GetMinTeleporterVersion(&_BatchCrossChainMessenger.CallOpts) +} + +// IsTeleporterAddressPaused is a free data retrieval call binding the contract method 0x97314297. +// +// Solidity: function isTeleporterAddressPaused(address teleporterAddress) view returns(bool) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCaller) IsTeleporterAddressPaused(opts *bind.CallOpts, teleporterAddress common.Address) (bool, error) { + var out []interface{} + err := _BatchCrossChainMessenger.contract.Call(opts, &out, "isTeleporterAddressPaused", teleporterAddress) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsTeleporterAddressPaused is a free data retrieval call binding the contract method 0x97314297. +// +// Solidity: function isTeleporterAddressPaused(address teleporterAddress) view returns(bool) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) IsTeleporterAddressPaused(teleporterAddress common.Address) (bool, error) { + return _BatchCrossChainMessenger.Contract.IsTeleporterAddressPaused(&_BatchCrossChainMessenger.CallOpts, teleporterAddress) +} + +// IsTeleporterAddressPaused is a free data retrieval call binding the contract method 0x97314297. +// +// Solidity: function isTeleporterAddressPaused(address teleporterAddress) view returns(bool) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCallerSession) IsTeleporterAddressPaused(teleporterAddress common.Address) (bool, error) { + return _BatchCrossChainMessenger.Contract.IsTeleporterAddressPaused(&_BatchCrossChainMessenger.CallOpts, teleporterAddress) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchCrossChainMessenger.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) Owner() (common.Address, error) { + return _BatchCrossChainMessenger.Contract.Owner(&_BatchCrossChainMessenger.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCallerSession) Owner() (common.Address, error) { + return _BatchCrossChainMessenger.Contract.Owner(&_BatchCrossChainMessenger.CallOpts) +} + +// TeleporterRegistry is a free data retrieval call binding the contract method 0x1a7f5bec. +// +// Solidity: function teleporterRegistry() view returns(address) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCaller) TeleporterRegistry(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _BatchCrossChainMessenger.contract.Call(opts, &out, "teleporterRegistry") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// TeleporterRegistry is a free data retrieval call binding the contract method 0x1a7f5bec. +// +// Solidity: function teleporterRegistry() view returns(address) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) TeleporterRegistry() (common.Address, error) { + return _BatchCrossChainMessenger.Contract.TeleporterRegistry(&_BatchCrossChainMessenger.CallOpts) +} + +// TeleporterRegistry is a free data retrieval call binding the contract method 0x1a7f5bec. +// +// Solidity: function teleporterRegistry() view returns(address) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCallerSession) TeleporterRegistry() (common.Address, error) { + return _BatchCrossChainMessenger.Contract.TeleporterRegistry(&_BatchCrossChainMessenger.CallOpts) +} + +// PauseTeleporterAddress is a paid mutator transaction binding the contract method 0x2b0d8f18. +// +// Solidity: function pauseTeleporterAddress(address teleporterAddress) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactor) PauseTeleporterAddress(opts *bind.TransactOpts, teleporterAddress common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.contract.Transact(opts, "pauseTeleporterAddress", teleporterAddress) +} + +// PauseTeleporterAddress is a paid mutator transaction binding the contract method 0x2b0d8f18. +// +// Solidity: function pauseTeleporterAddress(address teleporterAddress) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) PauseTeleporterAddress(teleporterAddress common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.PauseTeleporterAddress(&_BatchCrossChainMessenger.TransactOpts, teleporterAddress) +} + +// PauseTeleporterAddress is a paid mutator transaction binding the contract method 0x2b0d8f18. +// +// Solidity: function pauseTeleporterAddress(address teleporterAddress) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorSession) PauseTeleporterAddress(teleporterAddress common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.PauseTeleporterAddress(&_BatchCrossChainMessenger.TransactOpts, teleporterAddress) +} + +// ReceiveTeleporterMessage is a paid mutator transaction binding the contract method 0xc868efaa. +// +// Solidity: function receiveTeleporterMessage(bytes32 sourceBlockchainID, address originSenderAddress, bytes message) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactor) ReceiveTeleporterMessage(opts *bind.TransactOpts, sourceBlockchainID [32]byte, originSenderAddress common.Address, message []byte) (*types.Transaction, error) { + return _BatchCrossChainMessenger.contract.Transact(opts, "receiveTeleporterMessage", sourceBlockchainID, originSenderAddress, message) +} + +// ReceiveTeleporterMessage is a paid mutator transaction binding the contract method 0xc868efaa. +// +// Solidity: function receiveTeleporterMessage(bytes32 sourceBlockchainID, address originSenderAddress, bytes message) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) ReceiveTeleporterMessage(sourceBlockchainID [32]byte, originSenderAddress common.Address, message []byte) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.ReceiveTeleporterMessage(&_BatchCrossChainMessenger.TransactOpts, sourceBlockchainID, originSenderAddress, message) +} + +// ReceiveTeleporterMessage is a paid mutator transaction binding the contract method 0xc868efaa. +// +// Solidity: function receiveTeleporterMessage(bytes32 sourceBlockchainID, address originSenderAddress, bytes message) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorSession) ReceiveTeleporterMessage(sourceBlockchainID [32]byte, originSenderAddress common.Address, message []byte) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.ReceiveTeleporterMessage(&_BatchCrossChainMessenger.TransactOpts, sourceBlockchainID, originSenderAddress, message) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BatchCrossChainMessenger.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) RenounceOwnership() (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.RenounceOwnership(&_BatchCrossChainMessenger.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.RenounceOwnership(&_BatchCrossChainMessenger.TransactOpts) +} + +// SendMessages is a paid mutator transaction binding the contract method 0x3902970c. +// +// Solidity: function sendMessages(bytes32 destinationBlockchainID, address destinationAddress, address feeTokenAddress, uint256 feeAmount, uint256 requiredGasLimit, string[] messages) returns(bytes32[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactor) SendMessages(opts *bind.TransactOpts, destinationBlockchainID [32]byte, destinationAddress common.Address, feeTokenAddress common.Address, feeAmount *big.Int, requiredGasLimit *big.Int, messages []string) (*types.Transaction, error) { + return _BatchCrossChainMessenger.contract.Transact(opts, "sendMessages", destinationBlockchainID, destinationAddress, feeTokenAddress, feeAmount, requiredGasLimit, messages) +} + +// SendMessages is a paid mutator transaction binding the contract method 0x3902970c. +// +// Solidity: function sendMessages(bytes32 destinationBlockchainID, address destinationAddress, address feeTokenAddress, uint256 feeAmount, uint256 requiredGasLimit, string[] messages) returns(bytes32[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) SendMessages(destinationBlockchainID [32]byte, destinationAddress common.Address, feeTokenAddress common.Address, feeAmount *big.Int, requiredGasLimit *big.Int, messages []string) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.SendMessages(&_BatchCrossChainMessenger.TransactOpts, destinationBlockchainID, destinationAddress, feeTokenAddress, feeAmount, requiredGasLimit, messages) +} + +// SendMessages is a paid mutator transaction binding the contract method 0x3902970c. +// +// Solidity: function sendMessages(bytes32 destinationBlockchainID, address destinationAddress, address feeTokenAddress, uint256 feeAmount, uint256 requiredGasLimit, string[] messages) returns(bytes32[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorSession) SendMessages(destinationBlockchainID [32]byte, destinationAddress common.Address, feeTokenAddress common.Address, feeAmount *big.Int, requiredGasLimit *big.Int, messages []string) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.SendMessages(&_BatchCrossChainMessenger.TransactOpts, destinationBlockchainID, destinationAddress, feeTokenAddress, feeAmount, requiredGasLimit, messages) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.TransferOwnership(&_BatchCrossChainMessenger.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.TransferOwnership(&_BatchCrossChainMessenger.TransactOpts, newOwner) +} + +// UnpauseTeleporterAddress is a paid mutator transaction binding the contract method 0x4511243e. +// +// Solidity: function unpauseTeleporterAddress(address teleporterAddress) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactor) UnpauseTeleporterAddress(opts *bind.TransactOpts, teleporterAddress common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.contract.Transact(opts, "unpauseTeleporterAddress", teleporterAddress) +} + +// UnpauseTeleporterAddress is a paid mutator transaction binding the contract method 0x4511243e. +// +// Solidity: function unpauseTeleporterAddress(address teleporterAddress) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) UnpauseTeleporterAddress(teleporterAddress common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.UnpauseTeleporterAddress(&_BatchCrossChainMessenger.TransactOpts, teleporterAddress) +} + +// UnpauseTeleporterAddress is a paid mutator transaction binding the contract method 0x4511243e. +// +// Solidity: function unpauseTeleporterAddress(address teleporterAddress) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorSession) UnpauseTeleporterAddress(teleporterAddress common.Address) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.UnpauseTeleporterAddress(&_BatchCrossChainMessenger.TransactOpts, teleporterAddress) +} + +// UpdateMinTeleporterVersion is a paid mutator transaction binding the contract method 0x5eb99514. +// +// Solidity: function updateMinTeleporterVersion(uint256 version) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactor) UpdateMinTeleporterVersion(opts *bind.TransactOpts, version *big.Int) (*types.Transaction, error) { + return _BatchCrossChainMessenger.contract.Transact(opts, "updateMinTeleporterVersion", version) +} + +// UpdateMinTeleporterVersion is a paid mutator transaction binding the contract method 0x5eb99514. +// +// Solidity: function updateMinTeleporterVersion(uint256 version) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) UpdateMinTeleporterVersion(version *big.Int) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.UpdateMinTeleporterVersion(&_BatchCrossChainMessenger.TransactOpts, version) +} + +// UpdateMinTeleporterVersion is a paid mutator transaction binding the contract method 0x5eb99514. +// +// Solidity: function updateMinTeleporterVersion(uint256 version) returns() +func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorSession) UpdateMinTeleporterVersion(version *big.Int) (*types.Transaction, error) { + return _BatchCrossChainMessenger.Contract.UpdateMinTeleporterVersion(&_BatchCrossChainMessenger.TransactOpts, version) +} + +// BatchCrossChainMessengerMinTeleporterVersionUpdatedIterator is returned from FilterMinTeleporterVersionUpdated and is used to iterate over the raw logs and unpacked data for MinTeleporterVersionUpdated events raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerMinTeleporterVersionUpdatedIterator struct { + Event *BatchCrossChainMessengerMinTeleporterVersionUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub interfaces.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchCrossChainMessengerMinTeleporterVersionUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerMinTeleporterVersionUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerMinTeleporterVersionUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchCrossChainMessengerMinTeleporterVersionUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchCrossChainMessengerMinTeleporterVersionUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchCrossChainMessengerMinTeleporterVersionUpdated represents a MinTeleporterVersionUpdated event raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerMinTeleporterVersionUpdated struct { + OldMinTeleporterVersion *big.Int + NewMinTeleporterVersion *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMinTeleporterVersionUpdated is a free log retrieval operation binding the contract event 0xa9a7ef57e41f05b4c15480842f5f0c27edfcbb553fed281f7c4068452cc1c02d. +// +// Solidity: event MinTeleporterVersionUpdated(uint256 indexed oldMinTeleporterVersion, uint256 indexed newMinTeleporterVersion) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterMinTeleporterVersionUpdated(opts *bind.FilterOpts, oldMinTeleporterVersion []*big.Int, newMinTeleporterVersion []*big.Int) (*BatchCrossChainMessengerMinTeleporterVersionUpdatedIterator, error) { + + var oldMinTeleporterVersionRule []interface{} + for _, oldMinTeleporterVersionItem := range oldMinTeleporterVersion { + oldMinTeleporterVersionRule = append(oldMinTeleporterVersionRule, oldMinTeleporterVersionItem) + } + var newMinTeleporterVersionRule []interface{} + for _, newMinTeleporterVersionItem := range newMinTeleporterVersion { + newMinTeleporterVersionRule = append(newMinTeleporterVersionRule, newMinTeleporterVersionItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.FilterLogs(opts, "MinTeleporterVersionUpdated", oldMinTeleporterVersionRule, newMinTeleporterVersionRule) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerMinTeleporterVersionUpdatedIterator{contract: _BatchCrossChainMessenger.contract, event: "MinTeleporterVersionUpdated", logs: logs, sub: sub}, nil +} + +// WatchMinTeleporterVersionUpdated is a free log subscription operation binding the contract event 0xa9a7ef57e41f05b4c15480842f5f0c27edfcbb553fed281f7c4068452cc1c02d. +// +// Solidity: event MinTeleporterVersionUpdated(uint256 indexed oldMinTeleporterVersion, uint256 indexed newMinTeleporterVersion) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchMinTeleporterVersionUpdated(opts *bind.WatchOpts, sink chan<- *BatchCrossChainMessengerMinTeleporterVersionUpdated, oldMinTeleporterVersion []*big.Int, newMinTeleporterVersion []*big.Int) (event.Subscription, error) { + + var oldMinTeleporterVersionRule []interface{} + for _, oldMinTeleporterVersionItem := range oldMinTeleporterVersion { + oldMinTeleporterVersionRule = append(oldMinTeleporterVersionRule, oldMinTeleporterVersionItem) + } + var newMinTeleporterVersionRule []interface{} + for _, newMinTeleporterVersionItem := range newMinTeleporterVersion { + newMinTeleporterVersionRule = append(newMinTeleporterVersionRule, newMinTeleporterVersionItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.WatchLogs(opts, "MinTeleporterVersionUpdated", oldMinTeleporterVersionRule, newMinTeleporterVersionRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchCrossChainMessengerMinTeleporterVersionUpdated) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "MinTeleporterVersionUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseMinTeleporterVersionUpdated is a log parse operation binding the contract event 0xa9a7ef57e41f05b4c15480842f5f0c27edfcbb553fed281f7c4068452cc1c02d. +// +// Solidity: event MinTeleporterVersionUpdated(uint256 indexed oldMinTeleporterVersion, uint256 indexed newMinTeleporterVersion) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseMinTeleporterVersionUpdated(log types.Log) (*BatchCrossChainMessengerMinTeleporterVersionUpdated, error) { + event := new(BatchCrossChainMessengerMinTeleporterVersionUpdated) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "MinTeleporterVersionUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchCrossChainMessengerOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerOwnershipTransferredIterator struct { + Event *BatchCrossChainMessengerOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub interfaces.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchCrossChainMessengerOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchCrossChainMessengerOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchCrossChainMessengerOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchCrossChainMessengerOwnershipTransferred represents a OwnershipTransferred event raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*BatchCrossChainMessengerOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerOwnershipTransferredIterator{contract: _BatchCrossChainMessenger.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *BatchCrossChainMessengerOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchCrossChainMessengerOwnershipTransferred) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseOwnershipTransferred(log types.Log) (*BatchCrossChainMessengerOwnershipTransferred, error) { + event := new(BatchCrossChainMessengerOwnershipTransferred) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchCrossChainMessengerReceiveMessagesIterator is returned from FilterReceiveMessages and is used to iterate over the raw logs and unpacked data for ReceiveMessages events raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerReceiveMessagesIterator struct { + Event *BatchCrossChainMessengerReceiveMessages // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub interfaces.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchCrossChainMessengerReceiveMessagesIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerReceiveMessages) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerReceiveMessages) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchCrossChainMessengerReceiveMessagesIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchCrossChainMessengerReceiveMessagesIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchCrossChainMessengerReceiveMessages represents a ReceiveMessages event raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerReceiveMessages struct { + SourceBlockchainID [32]byte + OriginSenderAddress common.Address + Messages []string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterReceiveMessages is a free log retrieval operation binding the contract event 0x903a123a8d947dcbd005d05b20bb5180f878c6d3527681c1abb1d8987714cb9d. +// +// Solidity: event ReceiveMessages(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string[] messages) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterReceiveMessages(opts *bind.FilterOpts, sourceBlockchainID [][32]byte, originSenderAddress []common.Address) (*BatchCrossChainMessengerReceiveMessagesIterator, error) { + + var sourceBlockchainIDRule []interface{} + for _, sourceBlockchainIDItem := range sourceBlockchainID { + sourceBlockchainIDRule = append(sourceBlockchainIDRule, sourceBlockchainIDItem) + } + var originSenderAddressRule []interface{} + for _, originSenderAddressItem := range originSenderAddress { + originSenderAddressRule = append(originSenderAddressRule, originSenderAddressItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.FilterLogs(opts, "ReceiveMessages", sourceBlockchainIDRule, originSenderAddressRule) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerReceiveMessagesIterator{contract: _BatchCrossChainMessenger.contract, event: "ReceiveMessages", logs: logs, sub: sub}, nil +} + +// WatchReceiveMessages is a free log subscription operation binding the contract event 0x903a123a8d947dcbd005d05b20bb5180f878c6d3527681c1abb1d8987714cb9d. +// +// Solidity: event ReceiveMessages(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string[] messages) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchReceiveMessages(opts *bind.WatchOpts, sink chan<- *BatchCrossChainMessengerReceiveMessages, sourceBlockchainID [][32]byte, originSenderAddress []common.Address) (event.Subscription, error) { + + var sourceBlockchainIDRule []interface{} + for _, sourceBlockchainIDItem := range sourceBlockchainID { + sourceBlockchainIDRule = append(sourceBlockchainIDRule, sourceBlockchainIDItem) + } + var originSenderAddressRule []interface{} + for _, originSenderAddressItem := range originSenderAddress { + originSenderAddressRule = append(originSenderAddressRule, originSenderAddressItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.WatchLogs(opts, "ReceiveMessages", sourceBlockchainIDRule, originSenderAddressRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchCrossChainMessengerReceiveMessages) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "ReceiveMessages", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseReceiveMessages is a log parse operation binding the contract event 0x903a123a8d947dcbd005d05b20bb5180f878c6d3527681c1abb1d8987714cb9d. +// +// Solidity: event ReceiveMessages(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string[] messages) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseReceiveMessages(log types.Log) (*BatchCrossChainMessengerReceiveMessages, error) { + event := new(BatchCrossChainMessengerReceiveMessages) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "ReceiveMessages", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchCrossChainMessengerSendMessagesIterator is returned from FilterSendMessages and is used to iterate over the raw logs and unpacked data for SendMessages events raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerSendMessagesIterator struct { + Event *BatchCrossChainMessengerSendMessages // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub interfaces.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchCrossChainMessengerSendMessagesIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerSendMessages) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerSendMessages) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchCrossChainMessengerSendMessagesIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchCrossChainMessengerSendMessagesIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchCrossChainMessengerSendMessages represents a SendMessages event raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerSendMessages struct { + DestinationBlockchainID [32]byte + DestinationAddress common.Address + FeeTokenAddress common.Address + FeeAmount *big.Int + RequiredGasLimit *big.Int + Messages []string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSendMessages is a free log retrieval operation binding the contract event 0x430d1906813fdb2129a19139f4112a1396804605501a798df3a4042590ba20d5. +// +// Solidity: event SendMessages(bytes32 indexed destinationBlockchainID, address indexed destinationAddress, address feeTokenAddress, uint256 feeAmount, uint256 requiredGasLimit, string[] messages) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterSendMessages(opts *bind.FilterOpts, destinationBlockchainID [][32]byte, destinationAddress []common.Address) (*BatchCrossChainMessengerSendMessagesIterator, error) { + + var destinationBlockchainIDRule []interface{} + for _, destinationBlockchainIDItem := range destinationBlockchainID { + destinationBlockchainIDRule = append(destinationBlockchainIDRule, destinationBlockchainIDItem) + } + var destinationAddressRule []interface{} + for _, destinationAddressItem := range destinationAddress { + destinationAddressRule = append(destinationAddressRule, destinationAddressItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.FilterLogs(opts, "SendMessages", destinationBlockchainIDRule, destinationAddressRule) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerSendMessagesIterator{contract: _BatchCrossChainMessenger.contract, event: "SendMessages", logs: logs, sub: sub}, nil +} + +// WatchSendMessages is a free log subscription operation binding the contract event 0x430d1906813fdb2129a19139f4112a1396804605501a798df3a4042590ba20d5. +// +// Solidity: event SendMessages(bytes32 indexed destinationBlockchainID, address indexed destinationAddress, address feeTokenAddress, uint256 feeAmount, uint256 requiredGasLimit, string[] messages) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchSendMessages(opts *bind.WatchOpts, sink chan<- *BatchCrossChainMessengerSendMessages, destinationBlockchainID [][32]byte, destinationAddress []common.Address) (event.Subscription, error) { + + var destinationBlockchainIDRule []interface{} + for _, destinationBlockchainIDItem := range destinationBlockchainID { + destinationBlockchainIDRule = append(destinationBlockchainIDRule, destinationBlockchainIDItem) + } + var destinationAddressRule []interface{} + for _, destinationAddressItem := range destinationAddress { + destinationAddressRule = append(destinationAddressRule, destinationAddressItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.WatchLogs(opts, "SendMessages", destinationBlockchainIDRule, destinationAddressRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchCrossChainMessengerSendMessages) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "SendMessages", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSendMessages is a log parse operation binding the contract event 0x430d1906813fdb2129a19139f4112a1396804605501a798df3a4042590ba20d5. +// +// Solidity: event SendMessages(bytes32 indexed destinationBlockchainID, address indexed destinationAddress, address feeTokenAddress, uint256 feeAmount, uint256 requiredGasLimit, string[] messages) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseSendMessages(log types.Log) (*BatchCrossChainMessengerSendMessages, error) { + event := new(BatchCrossChainMessengerSendMessages) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "SendMessages", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchCrossChainMessengerTeleporterAddressPausedIterator is returned from FilterTeleporterAddressPaused and is used to iterate over the raw logs and unpacked data for TeleporterAddressPaused events raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerTeleporterAddressPausedIterator struct { + Event *BatchCrossChainMessengerTeleporterAddressPaused // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub interfaces.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchCrossChainMessengerTeleporterAddressPausedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerTeleporterAddressPaused) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerTeleporterAddressPaused) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchCrossChainMessengerTeleporterAddressPausedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchCrossChainMessengerTeleporterAddressPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchCrossChainMessengerTeleporterAddressPaused represents a TeleporterAddressPaused event raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerTeleporterAddressPaused struct { + TeleporterAddress common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTeleporterAddressPaused is a free log retrieval operation binding the contract event 0x933f93e57a222e6330362af8b376d0a8725b6901e9a2fb86d00f169702b28a4c. +// +// Solidity: event TeleporterAddressPaused(address indexed teleporterAddress) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterTeleporterAddressPaused(opts *bind.FilterOpts, teleporterAddress []common.Address) (*BatchCrossChainMessengerTeleporterAddressPausedIterator, error) { + + var teleporterAddressRule []interface{} + for _, teleporterAddressItem := range teleporterAddress { + teleporterAddressRule = append(teleporterAddressRule, teleporterAddressItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.FilterLogs(opts, "TeleporterAddressPaused", teleporterAddressRule) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerTeleporterAddressPausedIterator{contract: _BatchCrossChainMessenger.contract, event: "TeleporterAddressPaused", logs: logs, sub: sub}, nil +} + +// WatchTeleporterAddressPaused is a free log subscription operation binding the contract event 0x933f93e57a222e6330362af8b376d0a8725b6901e9a2fb86d00f169702b28a4c. +// +// Solidity: event TeleporterAddressPaused(address indexed teleporterAddress) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchTeleporterAddressPaused(opts *bind.WatchOpts, sink chan<- *BatchCrossChainMessengerTeleporterAddressPaused, teleporterAddress []common.Address) (event.Subscription, error) { + + var teleporterAddressRule []interface{} + for _, teleporterAddressItem := range teleporterAddress { + teleporterAddressRule = append(teleporterAddressRule, teleporterAddressItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.WatchLogs(opts, "TeleporterAddressPaused", teleporterAddressRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchCrossChainMessengerTeleporterAddressPaused) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "TeleporterAddressPaused", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTeleporterAddressPaused is a log parse operation binding the contract event 0x933f93e57a222e6330362af8b376d0a8725b6901e9a2fb86d00f169702b28a4c. +// +// Solidity: event TeleporterAddressPaused(address indexed teleporterAddress) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseTeleporterAddressPaused(log types.Log) (*BatchCrossChainMessengerTeleporterAddressPaused, error) { + event := new(BatchCrossChainMessengerTeleporterAddressPaused) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "TeleporterAddressPaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BatchCrossChainMessengerTeleporterAddressUnpausedIterator is returned from FilterTeleporterAddressUnpaused and is used to iterate over the raw logs and unpacked data for TeleporterAddressUnpaused events raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerTeleporterAddressUnpausedIterator struct { + Event *BatchCrossChainMessengerTeleporterAddressUnpaused // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub interfaces.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BatchCrossChainMessengerTeleporterAddressUnpausedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerTeleporterAddressUnpaused) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BatchCrossChainMessengerTeleporterAddressUnpaused) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BatchCrossChainMessengerTeleporterAddressUnpausedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BatchCrossChainMessengerTeleporterAddressUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BatchCrossChainMessengerTeleporterAddressUnpaused represents a TeleporterAddressUnpaused event raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerTeleporterAddressUnpaused struct { + TeleporterAddress common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTeleporterAddressUnpaused is a free log retrieval operation binding the contract event 0x844e2f3154214672229235858fd029d1dfd543901c6d05931f0bc2480a2d72c3. +// +// Solidity: event TeleporterAddressUnpaused(address indexed teleporterAddress) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterTeleporterAddressUnpaused(opts *bind.FilterOpts, teleporterAddress []common.Address) (*BatchCrossChainMessengerTeleporterAddressUnpausedIterator, error) { + + var teleporterAddressRule []interface{} + for _, teleporterAddressItem := range teleporterAddress { + teleporterAddressRule = append(teleporterAddressRule, teleporterAddressItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.FilterLogs(opts, "TeleporterAddressUnpaused", teleporterAddressRule) + if err != nil { + return nil, err + } + return &BatchCrossChainMessengerTeleporterAddressUnpausedIterator{contract: _BatchCrossChainMessenger.contract, event: "TeleporterAddressUnpaused", logs: logs, sub: sub}, nil +} + +// WatchTeleporterAddressUnpaused is a free log subscription operation binding the contract event 0x844e2f3154214672229235858fd029d1dfd543901c6d05931f0bc2480a2d72c3. +// +// Solidity: event TeleporterAddressUnpaused(address indexed teleporterAddress) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchTeleporterAddressUnpaused(opts *bind.WatchOpts, sink chan<- *BatchCrossChainMessengerTeleporterAddressUnpaused, teleporterAddress []common.Address) (event.Subscription, error) { + + var teleporterAddressRule []interface{} + for _, teleporterAddressItem := range teleporterAddress { + teleporterAddressRule = append(teleporterAddressRule, teleporterAddressItem) + } + + logs, sub, err := _BatchCrossChainMessenger.contract.WatchLogs(opts, "TeleporterAddressUnpaused", teleporterAddressRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BatchCrossChainMessengerTeleporterAddressUnpaused) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "TeleporterAddressUnpaused", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTeleporterAddressUnpaused is a log parse operation binding the contract event 0x844e2f3154214672229235858fd029d1dfd543901c6d05931f0bc2480a2d72c3. +// +// Solidity: event TeleporterAddressUnpaused(address indexed teleporterAddress) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseTeleporterAddressUnpaused(log types.Log) (*BatchCrossChainMessengerTeleporterAddressUnpaused, error) { + event := new(BatchCrossChainMessengerTeleporterAddressUnpaused) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "TeleporterAddressUnpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/tests/batch_relay.go b/tests/batch_relay.go new file mode 100644 index 00000000..200783a4 --- /dev/null +++ b/tests/batch_relay.go @@ -0,0 +1,110 @@ +package tests + +import ( + "context" + "math/big" + "time" + + 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" + examplecrosschainmessenger "github.com/ava-labs/teleporter/abi-bindings/go/CrossChainApplications/examples/ExampleMessenger/ExampleCrossChainMessenger" + "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" +) + +// Processes multiple Warp messages contained in the same block +func BatchRelay(network interfaces.LocalNetwork) { + subnetAInfo := network.GetPrimaryNetworkInfo() + subnetBInfo, _ := utils.GetTwoSubnets(network) + fundedAddress, fundedKey := network.GetFundedAccountInfo() + teleporterContractAddress := network.GetTeleporterContractAddress() + err := testUtils.ClearRelayerStorage() + Expect(err).Should(BeNil()) + + // + // Deploy the batch messenger contracts + // + ctx := context.Background() + _, batchMessengerA := testUtils.DeployBatchCrossChainMessenger( + ctx, + fundedKey, + fundedAddress, + subnetAInfo, + ) + batchMessengerAddressB, batchMessengerB := testUtils.DeployBatchCrossChainMessenger( + ctx, + fundedKey, + fundedAddress, + subnetBInfo, + ) + + // + // Fund the relayer address on all subnets + // + + 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) + + // + // Set up relayer config + // + relayerConfig := testUtils.CreateDefaultRelayerConfig( + []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, + []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, + teleporterContractAddress, + fundedAddress, + relayerKey, + ) + + 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) + + // + // Send a batch message from subnet A -> B + // + + newHeadsDest := make(chan *types.Header, 10) + sub, err := subnetBInfo.WSClient.SubscribeNewHead(ctx, newHeadsDest) + Expect(err).Should(BeNil()) + defer sub.Unsubscribe() + + messages := []string{"hello", "world", "this", "is", "a", "cross", "chain", "batch", "message"} + + optsA, err := bind.NewKeyedTransactorWithChainID(fundedKey, subnetAInfo.EVMChainID) + Expect(err).Should(BeNil()) + tx, err := batchMessengerA.SendMessages( + optsA, + subnetBInfo.BlockchainID, + batchMessengerAddressB, + common.BigToAddress(common.Big0), + big.NewInt(0), + big.NewInt(0).Mul(examplecrosschainmessenger.SendMessageRequiredGas, big.NewInt(10)), + messages, + ) + Expect(err).Should(BeNil()) + + utils.WaitForTransactionSuccess(ctx, subnetAInfo, tx.Hash()) + + // Wait for the message on the destination + <-newHeadsDest + + _, receivedMessages, err := batchMessengerB.GetCurrentMessages(&bind.CallOpts{}, subnetAInfo.BlockchainID) + Expect(err).Should(BeNil()) + for i, msg := range receivedMessages { + Expect(msg).Should(Equal(messages[i])) + } +} diff --git a/tests/contracts/foundry.toml b/tests/contracts/foundry.toml new file mode 100644 index 00000000..5607c03a --- /dev/null +++ b/tests/contracts/foundry.toml @@ -0,0 +1,26 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +solc_version = '0.8.18' +test = 'tests' + +[fmt] +line_length = 100 +tab_width = 4 +bracket_spacing = false +int_types = 'preserve' +multiline_func_header = 'params_first' +quote_style = 'double' +number_underscores = 'thousands' +override_spacing = true +wrap_comments = false + +# Add the following to your VSCode settings to enable automatic formatting. +# { +# "editor.formatOnSave": true, +# "[solidity]": { +# "editor.defaultFormatter": "JuanBlanco.solidity" +# }, +# "solidity.formatter": "forge", +# } diff --git a/tests/contracts/lib/forge-std b/tests/contracts/lib/forge-std new file mode 160000 index 00000000..d44c4fbb --- /dev/null +++ b/tests/contracts/lib/forge-std @@ -0,0 +1 @@ +Subproject commit d44c4fbbb9ff054fb334babbdd34f9b6e899b3d6 diff --git a/tests/contracts/lib/teleporter b/tests/contracts/lib/teleporter new file mode 160000 index 00000000..8b7d7245 --- /dev/null +++ b/tests/contracts/lib/teleporter @@ -0,0 +1 @@ +Subproject commit 8b7d7245d6998f14a0655b86670c998ba5e5f964 diff --git a/tests/contracts/remappings.txt b/tests/contracts/remappings.txt new file mode 100644 index 00000000..35224d95 --- /dev/null +++ b/tests/contracts/remappings.txt @@ -0,0 +1,3 @@ +@openzeppelin/contracts@4.8.1/=lib/teleporter/contracts/lib/openzeppelin-contracts/contracts/ +@avalabs/subnet-evm-contracts@1.2.0/=lib/teleporter/contracts/lib/subnet-evm/contracts/ +@teleporter/=lib/teleporter/contracts/src/Teleporter/ diff --git a/tests/contracts/src/BatchCrossChainMessenger.sol b/tests/contracts/src/BatchCrossChainMessenger.sol new file mode 100644 index 00000000..eea360cf --- /dev/null +++ b/tests/contracts/src/BatchCrossChainMessenger.sol @@ -0,0 +1,129 @@ +// (c) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// SPDX-License-Identifier: Ecosystem + +pragma solidity 0.8.18; + +import {TeleporterMessageInput, TeleporterFeeInfo} from "@teleporter/ITeleporterMessenger.sol"; +import {SafeERC20TransferFrom, SafeERC20} from "@teleporter/SafeERC20TransferFrom.sol"; +import {TeleporterOwnerUpgradeable} from "@teleporter/upgrades/TeleporterOwnerUpgradeable.sol"; +import {IERC20} from "@openzeppelin/contracts@4.8.1/token/ERC20/IERC20.sol"; +import {ReentrancyGuard} from "@openzeppelin/contracts@4.8.1/security/ReentrancyGuard.sol"; + +/** + * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. + * DO NOT USE THIS CODE IN PRODUCTION. + */ + +/** + * @dev BatchCrossChainMessenger batches multiple Teleporter messages into a single transaction + */ +contract BatchCrossChainMessenger is ReentrancyGuard, TeleporterOwnerUpgradeable { + using SafeERC20 for IERC20; + + // Messages sent to this contract. + struct Messages { + address sender; + string[] messages; + } + + mapping(bytes32 sourceBlockchainID => Messages messages) private _messages; + + /** + * @dev Emitted when a message is submited to be sent. + */ + event SendMessages( + bytes32 indexed destinationBlockchainID, + address indexed destinationAddress, + address feeTokenAddress, + uint256 feeAmount, + uint256 requiredGasLimit, + string[] messages + ); + + /** + * @dev Emitted when a new message is received from a given chain ID. + */ + event ReceiveMessages( + bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string[] messages + ); + + constructor( + address teleporterRegistryAddress, + address teleporterManager + ) TeleporterOwnerUpgradeable(teleporterRegistryAddress, teleporterManager) {} + + /** + * @dev Sends a message to another chain. + * @return The message ID of the newly sent message. + */ + function sendMessages( + bytes32 destinationBlockchainID, + address destinationAddress, + address feeTokenAddress, + uint256 feeAmount, + uint256 requiredGasLimit, + string[] memory messages + ) external nonReentrant returns (bytes32[] memory) { + // For non-zero fee amounts, first transfer the fee to this contract. + uint256 adjustedFeeAmount; + if (feeAmount > 0) { + adjustedFeeAmount = + SafeERC20TransferFrom.safeTransferFrom(IERC20(feeTokenAddress), feeAmount); + } + + emit SendMessages({ + destinationBlockchainID: destinationBlockchainID, + destinationAddress: destinationAddress, + feeTokenAddress: feeTokenAddress, + feeAmount: adjustedFeeAmount, + requiredGasLimit: requiredGasLimit, + messages: messages + }); + bytes32[] memory messageIDs = new bytes32[](messages.length); + for (uint256 i = 0; i < messages.length; i++) { + bytes32 messageID = _sendTeleporterMessage( + TeleporterMessageInput({ + destinationBlockchainID: destinationBlockchainID, + destinationAddress: destinationAddress, + feeInfo: TeleporterFeeInfo({feeTokenAddress: feeTokenAddress, amount: adjustedFeeAmount}), + requiredGasLimit: requiredGasLimit, + allowedRelayerAddresses: new address[](0), + message: abi.encode(messages[i]) + }) + ); + messageIDs[i] = messageID; + } + return messageIDs; + } + + /** + * @dev Returns the current message from another chain. + * @return The sender of the message, and the message itself. + */ + function getCurrentMessages(bytes32 sourceBlockchainID) + external + view + returns (address, string[] memory) + { + Messages memory messageInfo = _messages[sourceBlockchainID]; + return (messageInfo.sender, messageInfo.messages); + } + + /** + * @dev See {TeleporterUpgradeable-receiveTeleporterMessage}. + * + * Receives a message from another chain. + */ + function _receiveTeleporterMessage( + bytes32 sourceBlockchainID, + address originSenderAddress, + bytes memory messages + ) internal override { + // Store the message. + string[] memory messagesString = abi.decode(messages, (string[])); + _messages[sourceBlockchainID] = Messages(originSenderAddress, messagesString); + emit ReceiveMessages(sourceBlockchainID, originSenderAddress, messagesString); + } +} diff --git a/tests/e2e_test.go b/tests/e2e_test.go index b5c6ffa1..1e1b8df5 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -77,4 +77,7 @@ var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { ginkgo.It("Allowed Addresses", func() { AllowedAddresses(localNetworkInstance) }) + ginkgo.It("Batch Message", func() { + BatchRelay(localNetworkInstance) + }) }) diff --git a/tests/utils/utils.go b/tests/utils/utils.go index 1b9ff1a1..abab1e78 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -21,6 +21,7 @@ import ( warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/awm-relayer/config" offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" + batchcrosschainmessenger "github.com/ava-labs/awm-relayer/tests/abi-bindings/go/BatchCrossChainMessenger" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/precompile/contracts/warp" @@ -451,3 +452,26 @@ func TriggerProcessMissedBlocks( Expect(delivered2).Should(BeFalse()) Expect(delivered3).Should(BeTrue()) } + +func DeployBatchCrossChainMessenger( + ctx context.Context, + senderKey *ecdsa.PrivateKey, + teleporterManager common.Address, + subnet interfaces.SubnetTestInfo, +) (common.Address, *batchcrosschainmessenger.BatchCrossChainMessenger) { + opts, err := bind.NewKeyedTransactorWithChainID( + senderKey, subnet.EVMChainID) + Expect(err).Should(BeNil()) + address, tx, exampleMessenger, err := batchcrosschainmessenger.DeployBatchCrossChainMessenger( + opts, + subnet.RPCClient, + subnet.TeleporterRegistryAddress, + teleporterManager, + ) + Expect(err).Should(BeNil()) + + // Wait for the transaction to be mined + utils.WaitForTransactionSuccess(ctx, subnet, tx.Hash()) + + return address, exampleMessenger +} From 1a051f32ab188ce90635515c8bd5030def4d2b2f Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 14:45:44 -0500 Subject: [PATCH 21/49] do not assume ordered message delivery --- tests/batch_relay.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/batch_relay.go b/tests/batch_relay.go index 200783a4..906161eb 100644 --- a/tests/batch_relay.go +++ b/tests/batch_relay.go @@ -5,6 +5,7 @@ import ( "math/big" "time" + "github.com/ava-labs/avalanchego/utils/set" 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" @@ -83,6 +84,10 @@ func BatchRelay(network interfaces.LocalNetwork) { defer sub.Unsubscribe() messages := []string{"hello", "world", "this", "is", "a", "cross", "chain", "batch", "message"} + sentMessages := set.NewSet[string](len(messages)) + for _, msg := range messages { + sentMessages.Add(msg) + } optsA, err := bind.NewKeyedTransactorWithChainID(fundedKey, subnetAInfo.EVMChainID) Expect(err).Should(BeNil()) @@ -104,7 +109,7 @@ func BatchRelay(network interfaces.LocalNetwork) { _, receivedMessages, err := batchMessengerB.GetCurrentMessages(&bind.CallOpts{}, subnetAInfo.BlockchainID) Expect(err).Should(BeNil()) - for i, msg := range receivedMessages { - Expect(msg).Should(Equal(messages[i])) + for _, msg := range receivedMessages { + Expect(sentMessages.Contains(msg)).To(BeTrue()) } } From 2db03ecf184dac5c65dcfaeaba800167721a8de4 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 14:46:08 -0500 Subject: [PATCH 22/49] migrate deprecated flag --- .golangci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 8ea94988..3a7ec484 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,13 +3,13 @@ run: timeout: 3m tests: true - # skip auto-generated files. - skip-files: - - ".*mock.*" issues: # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. max-same-issues: 0 + # skip auto-generated files. + exclude-files: + - ".*mock.*" linters: disable-all: true From 248c852601b1752e173abc87fbde8054bb573fb1 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 14:50:31 -0500 Subject: [PATCH 23/49] checkout submodules in ci --- .github/workflows/codeql.yml | 2 ++ .github/workflows/e2e.yml | 5 ++--- .github/workflows/linter.yml | 2 ++ .github/workflows/release.yml | 1 + .github/workflows/snyk.yml | 1 + .github/workflows/test.yml | 2 ++ 6 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 95b568b4..de527337 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -37,6 +37,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: recursive # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 64890691..fa1f6c24 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -19,6 +19,8 @@ jobs: steps: - name: Checkout awm-relayer repository uses: actions/checkout@v4 + with: + submodules: recursive - name: Set Go version run: | @@ -43,8 +45,5 @@ jobs: - 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 - - 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 68dab997..2caa0928 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -16,6 +16,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: recursive - name: Set Go version run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 55ffa03c..8fda2b29 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,7 @@ jobs: with: fetch-depth: 0 path: awm-relayer + submodules: recursive - name: Set Go version run: | diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 24055925..409298da 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -18,6 +18,7 @@ jobs: uses: actions/checkout@v4 with: path: awm-relayer + submodules: recursive - name: Run Snyk uses: snyk/actions/golang@master diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 15651e28..ca1661e2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,8 @@ jobs: steps: - name: Checkout awm-relayer repository uses: actions/checkout@v4 + with: + submodules: recursive - name: Set Go version run: | From b5cd801075e02a8a5b9e0dc8de1a429b137dca8c Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 14:54:01 -0500 Subject: [PATCH 24/49] re-checkout repo --- .github/workflows/e2e.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index fa1f6c24..6a930fe3 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -45,5 +45,10 @@ jobs: - 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 From 4d0cd01c7539e46d5b1f7b07a7416b23cf44ec6e Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 16:33:39 -0500 Subject: [PATCH 25/49] remove unused param --- main/main.go | 2 +- messages/message_manager.go | 2 ++ relayer/listener.go | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/main/main.go b/main/main.go index e94a0b9b..94e52d73 100644 --- a/main/main.go +++ b/main/main.go @@ -359,7 +359,7 @@ func runRelayer( zap.String("blockchainID", sourceBlockchain.BlockchainID), zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), ) - appRelayer, handler, err := listener.GetAppRelayerMessageHandler(0, warpMessage) + appRelayer, handler, err := listener.GetAppRelayerMessageHandler(warpMessage) if err != nil { logger.Error( "Failed to parse manual Warp message. Continuing.", diff --git a/messages/message_manager.go b/messages/message_manager.go index 043eab24..7d5a39cd 100644 --- a/messages/message_manager.go +++ b/messages/message_manager.go @@ -14,9 +14,11 @@ import ( // MessageManager is specific to each message protocol. The interface handles choosing which messages to send // for each message protocol, and performs the sending to the destination chain. type MessageManager interface { + // Create a message handler to relay the Warp message NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (MessageHandler, error) } +// MessageHandlers relay a single Warp message. A new instance should be created for each Warp message. type MessageHandler interface { // ShouldSendMessage returns true if the message should be sent to the destination chain // If an error is returned, the boolean should be ignored by the caller. diff --git a/relayer/listener.go b/relayer/listener.go index 3b9d0d3a..d7a9cd6e 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -234,7 +234,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) 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 := lstnr.GetAppRelayerMessageHandler(block.BlockNumber, warpLogInfo) + appRelayer, handler, err := lstnr.GetAppRelayerMessageHandler(warpLogInfo) if err != nil { lstnr.logger.Error( "Failed to parse message", @@ -364,7 +364,7 @@ func (lstnr *Listener) getApplicationRelayer( } // TODONOW: Does this function make sense? There's probably a better way to organize this logic. -func (lstnr *Listener) GetAppRelayerMessageHandler(height uint64, warpMessageInfo *relayerTypes.WarpMessageInfo) ( +func (lstnr *Listener) GetAppRelayerMessageHandler(warpMessageInfo *relayerTypes.WarpMessageInfo) ( *ApplicationRelayer, messages.MessageHandler, error, From 1cd196ed99d390fe4a1bf27e408c8f802391a02b Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 16:33:51 -0500 Subject: [PATCH 26/49] relay between subnets, no c-chain --- tests/batch_relay.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/batch_relay.go b/tests/batch_relay.go index 906161eb..621de946 100644 --- a/tests/batch_relay.go +++ b/tests/batch_relay.go @@ -9,7 +9,6 @@ import ( 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" - examplecrosschainmessenger "github.com/ava-labs/teleporter/abi-bindings/go/CrossChainApplications/examples/ExampleMessenger/ExampleCrossChainMessenger" "github.com/ava-labs/teleporter/tests/interfaces" "github.com/ava-labs/teleporter/tests/utils" "github.com/ethereum/go-ethereum/common" @@ -20,8 +19,7 @@ import ( // Processes multiple Warp messages contained in the same block func BatchRelay(network interfaces.LocalNetwork) { - subnetAInfo := network.GetPrimaryNetworkInfo() - subnetBInfo, _ := utils.GetTwoSubnets(network) + subnetAInfo, subnetBInfo := utils.GetTwoSubnets(network) fundedAddress, fundedKey := network.GetFundedAccountInfo() teleporterContractAddress := network.GetTeleporterContractAddress() err := testUtils.ClearRelayerStorage() @@ -95,9 +93,9 @@ func BatchRelay(network interfaces.LocalNetwork) { optsA, subnetBInfo.BlockchainID, batchMessengerAddressB, - common.BigToAddress(common.Big0), + common.Address{}, big.NewInt(0), - big.NewInt(0).Mul(examplecrosschainmessenger.SendMessageRequiredGas, big.NewInt(10)), + big.NewInt(500000), messages, ) Expect(err).Should(BeNil()) From 7b345c658dfd1e365717fcb404b97fb16ea9df6b Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 18:38:11 -0500 Subject: [PATCH 27/49] debug logs --- relayer/application_relayer.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 50dfcddd..b1548ef2 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -142,12 +142,12 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, msgs []messages.Messag return err } r.checkpointManager.StageCommittedHeight(height) - r.logger.Debug( "Processed block", zap.Uint64("height", height), zap.String("sourceBlockchainID", r.relayerID.SourceBlockchainID.String()), zap.String("relayerID", r.relayerID.ID.String()), + zap.Int("numMessages", len(msgs)), ) return nil } @@ -177,6 +177,12 @@ func (r *ApplicationRelayer) relayMessage( messageHandler messages.MessageHandler, useAppRequestNetwork bool, ) error { + r.logger.Debug( + "Relaying message", + zap.Uint32("requestID", requestID), + zap.String("sourceBlockchainID", r.sourceBlockchain.BlockchainID), + zap.String("relayerID", r.relayerID.ID.String()), + ) shouldSend, err := messageHandler.ShouldSendMessage(r.relayerID.DestinationBlockchainID) if err != nil { r.logger.Error( From b3c5b5e31839a4768927a605f91c38bb934e2316 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 14 May 2024 18:38:49 -0500 Subject: [PATCH 28/49] batch test requires all messages delivered --- .../BatchCrossChainMessenger.go | 77 +++++++++---------- tests/batch_relay.go | 37 ++++++--- .../src/BatchCrossChainMessenger.sol | 20 ++--- 3 files changed, 73 insertions(+), 61 deletions(-) diff --git a/tests/abi-bindings/go/BatchCrossChainMessenger/BatchCrossChainMessenger.go b/tests/abi-bindings/go/BatchCrossChainMessenger/BatchCrossChainMessenger.go index d4a97ac2..2d28a91a 100644 --- a/tests/abi-bindings/go/BatchCrossChainMessenger/BatchCrossChainMessenger.go +++ b/tests/abi-bindings/go/BatchCrossChainMessenger/BatchCrossChainMessenger.go @@ -31,8 +31,8 @@ var ( // BatchCrossChainMessengerMetaData contains all meta data concerning the BatchCrossChainMessenger contract. var BatchCrossChainMessengerMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterRegistryAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"teleporterManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"oldMinTeleporterVersion\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"newMinTeleporterVersion\",\"type\":\"uint256\"}],\"name\":\"MinTeleporterVersionUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"originSenderAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"messages\",\"type\":\"string[]\"}],\"name\":\"ReceiveMessages\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"destinationBlockchainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeTokenAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"messages\",\"type\":\"string[]\"}],\"name\":\"SendMessages\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"TeleporterAddressPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"TeleporterAddressUnpaused\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"}],\"name\":\"getCurrentMessages\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMinTeleporterVersion\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"isTeleporterAddressPaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"pauseTeleporterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"originSenderAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"receiveTeleporterMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationBlockchainID\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"string[]\",\"name\":\"messages\",\"type\":\"string[]\"}],\"name\":\"sendMessages\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"teleporterRegistry\",\"outputs\":[{\"internalType\":\"contractTeleporterRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"unpauseTeleporterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"version\",\"type\":\"uint256\"}],\"name\":\"updateMinTeleporterVersion\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a06040523480156200001157600080fd5b50604051620020403803806200204083398101604081905262000034916200029f565b60016000558181816001600160a01b038116620000be5760405162461bcd60e51b815260206004820152603760248201527f54656c65706f727465725570677261646561626c653a207a65726f2074656c6560448201527f706f72746572207265676973747279206164647265737300000000000000000060648201526084015b60405180910390fd5b6001600160a01b03811660808190526040805163301fd1f560e21b8152905163c07f47d4916004808201926020929091908290030181865afa15801562000109573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200012f9190620002d7565b600255506200013e3362000153565b6200014981620001a5565b50505050620002f1565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b620001af62000224565b6001600160a01b038116620002165760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401620000b5565b620002218162000153565b50565b6003546001600160a01b03163314620002805760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620000b5565b565b80516001600160a01b03811681146200029a57600080fd5b919050565b60008060408385031215620002b357600080fd5b620002be8362000282565b9150620002ce6020840162000282565b90509250929050565b600060208284031215620002ea57600080fd5b5051919050565b608051611d1f620003216000396000818160be0152818161073701528181610c570152610fce0152611d1f6000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c80638da5cb5b116100715780638da5cb5b146101605780639731429714610171578063c1329fcb146101ad578063c868efaa146101ce578063d2cc7a70146101e1578063f2fde38b146101f257600080fd5b80631a7f5bec146100b95780632b0d8f18146100fd5780633902970c146101125780634511243e146101325780635eb9951414610145578063715018a614610158575b600080fd5b6100e07f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61011061010b3660046114c8565b610205565b005b610125610120366004611578565b61030a565b6040516100f491906116c3565b6101106101403660046114c8565b6104e8565b610110610153366004611707565b6105e5565b6101106105f9565b6003546001600160a01b03166100e0565b61019d61017f3660046114c8565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100f4565b6101c06101bb366004611707565b61060d565b6040516100f49291906117c5565b6101106101dc3660046117e9565b610722565b6002546040519081526020016100f4565b6101106102003660046114c8565b6108ec565b61020d610962565b6001600160a01b03811661023c5760405162461bcd60e51b815260040161023390611872565b60405180910390fd5b6001600160a01b03811660009081526001602052604090205460ff16156102bb5760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a2061646472657373206160448201526c1b1c9958591e481c185d5cd959609a1b6064820152608401610233565b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f933f93e57a222e6330362af8b376d0a8725b6901e9a2fb86d00f169702b28a4c9190a250565b606061031461096a565b600084156103295761032686866109c3565b90505b866001600160a01b0316887f430d1906813fdb2129a19139f4112a1396804605501a798df3a4042590ba20d58884888860405161036994939291906118c0565b60405180910390a36000835167ffffffffffffffff81111561038d5761038d6114e5565b6040519080825280602002602001820160405280156103b6578160200160208202803683370190505b50905060005b84518110156104d057600061049d6040518060c001604052808d81526020018c6001600160a01b0316815260200160405180604001604052808d6001600160a01b03168152602001888152508152602001898152602001600067ffffffffffffffff81111561042d5761042d6114e5565b604051908082528060200260200182016040528015610456578160200160208202803683370190505b50815260200188858151811061046e5761046e6118ed565b60200260200101516040516020016104869190611903565b604051602081830303815290604052815250610b2d565b9050808383815181106104b2576104b26118ed565b602090810291909101015250806104c88161192c565b9150506103bc565b509150506104de6001600055565b9695505050505050565b6104f0610962565b6001600160a01b0381166105165760405162461bcd60e51b815260040161023390611872565b6001600160a01b03811660009081526001602052604090205460ff166105905760405162461bcd60e51b815260206004820152602960248201527f54656c65706f727465725570677261646561626c653a2061646472657373206e6044820152681bdd081c185d5cd95960ba1b6064820152608401610233565b6040516001600160a01b038216907f844e2f3154214672229235858fd029d1dfd543901c6d05931f0bc2480a2d72c390600090a26001600160a01b03166000908152600160205260409020805460ff19169055565b6105ed610962565b6105f681610c53565b50565b610601610df3565b61060b6000610e4d565b565b60008181526004602090815260408083208151808301835281546001600160a01b0316815260018201805484518187028101870190955280855260609587959394938582019390929091879084015b8282101561070857838290600052602060002001805461067b90611945565b80601f01602080910402602001604051908101604052809291908181526020018280546106a790611945565b80156106f45780601f106106c9576101008083540402835291602001916106f4565b820191906000526020600020905b8154815290600101906020018083116106d757829003601f168201915b50505050508152602001906001019061065c565b505050915250508051602090910151909590945092505050565b61072a61096a565b6002546001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016634c1f08ce336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156107a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c5919061197f565b101561082c5760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201526f32b632b837b93a32b91039b2b73232b960811b6064820152608401610233565b6108353361017f565b1561089b5760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881859191c995cdcc81c185d5cd95960821b6064820152608401610233565b6108dc848484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e9f92505050565b6108e66001600055565b50505050565b6108f4610df3565b6001600160a01b0381166109595760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610233565b6105f681610e4d565b61060b610df3565b6002600054036109bc5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610233565b6002600055565b6040516370a0823160e01b815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa158015610a0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a30919061197f565b9050610a476001600160a01b038516333086610f5e565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015610a8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab2919061197f565b9050818111610b185760405162461bcd60e51b815260206004820152602c60248201527f5361666545524332305472616e7366657246726f6d3a2062616c616e6365206e60448201526b1bdd081a5b98dc99585cd95960a21b6064820152608401610233565b610b228282611998565b925050505b92915050565b600080610b38610fc9565b60408401516020015190915015610bdd576040830151516001600160a01b0316610bba5760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a207a65726f206665652060448201526c746f6b656e206164647265737360981b6064820152608401610233565b604083015160208101519051610bdd916001600160a01b039091169083906110dd565b604051630624488560e41b81526001600160a01b03821690636244885090610c099086906004016119ef565b6020604051808303816000875af1158015610c28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4c919061197f565b9392505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cb3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd7919061197f565b60025490915081831115610d475760405162461bcd60e51b815260206004820152603160248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201527032b632b837b93a32b9103b32b939b4b7b760791b6064820152608401610233565b808311610dbc5760405162461bcd60e51b815260206004820152603f60248201527f54656c65706f727465725570677261646561626c653a206e6f7420677265617460448201527f6572207468616e2063757272656e74206d696e696d756d2076657273696f6e006064820152608401610233565b6002839055604051839082907fa9a7ef57e41f05b4c15480842f5f0c27edfcbb553fed281f7c4068452cc1c02d90600090a3505050565b6003546001600160a01b0316331461060b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610233565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600081806020019051810190610eb59190611a6d565b6040805180820182526001600160a01b038681168252602080830185815260008a81526004835294909420835181546001600160a01b03191693169290921782559251805194955091939092610f129260018501929101906113f6565b50905050826001600160a01b0316847f903a123a8d947dcbd005d05b20bb5180f878c6d3527681c1abb1d8987714cb9d83604051610f509190611b5a565b60405180910390a350505050565b6040516001600160a01b03808516602483015283166044820152606481018290526108e69085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261118f565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d820e64f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561102a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104e9190611b6d565b9050611072816001600160a01b031660009081526001602052604090205460ff1690565b156110d85760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881cd95b991a5b99c81c185d5cd95960821b6064820152608401610233565b919050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa15801561112e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611152919061197f565b61115c9190611b8a565b6040516001600160a01b0385166024820152604481018290529091506108e690859063095ea7b360e01b90606401610f92565b60006111e4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166112669092919063ffffffff16565b80519091501561126157808060200190518101906112029190611b9d565b6112615760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610233565b505050565b6060611275848460008561127d565b949350505050565b6060824710156112de5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610233565b600080866001600160a01b031685876040516112fa9190611bbf565b60006040518083038185875af1925050503d8060008114611337576040519150601f19603f3d011682016040523d82523d6000602084013e61133c565b606091505b509150915061134d87838387611358565b979650505050505050565b606083156113c75782516000036113c0576001600160a01b0385163b6113c05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610233565b5081611275565b61127583838151156113dc5781518083602001fd5b8060405162461bcd60e51b81526004016102339190611903565b82805482825590600052602060002090810192821561143c579160200282015b8281111561143c578251829061142c9082611c29565b5091602001919060010190611416565b5061144892915061144c565b5090565b808211156114485760006114608282611469565b5060010161144c565b50805461147590611945565b6000825580601f10611485575050565b601f0160209004906000526020600020908101906105f691905b80821115611448576000815560010161149f565b6001600160a01b03811681146105f657600080fd5b6000602082840312156114da57600080fd5b8135610c4c816114b3565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611524576115246114e5565b604052919050565b600067ffffffffffffffff821115611546576115466114e5565b5060051b60200190565b600067ffffffffffffffff82111561156a5761156a6114e5565b50601f01601f191660200190565b60008060008060008060c0878903121561159157600080fd5b863595506115a260208801356114b3565b602087013594506115b660408801356114b3565b60408701359350606087013592506080870135915067ffffffffffffffff60a088013511156115e457600080fd5b60a0870135870188601f8201126115fa57600080fd5b61160c611607823561152c565b6114fb565b81358082526020808301929160051b8401018b81111561162b57600080fd5b602084015b818110156116b15767ffffffffffffffff8135111561164e57600080fd5b803585018d603f82011261166157600080fd5b602081013561167261160782611550565b8181528f604083850101111561168757600080fd5b81604084016020830137600060208383010152808752505050602084019350602081019050611630565b50508093505050509295509295509295565b6020808252825182820181905260009190848201906040850190845b818110156116fb578351835292840192918401916001016116df565b50909695505050505050565b60006020828403121561171957600080fd5b5035919050565b60005b8381101561173b578181015183820152602001611723565b50506000910152565b6000815180845261175c816020860160208601611720565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b858110156117b85782840389526117a6848351611744565b9885019893509084019060010161178e565b5091979650505050505050565b6001600160a01b038316815260406020820181905260009061127590830184611770565b600080600080606085870312156117ff57600080fd5b843593506020850135611811816114b3565b9250604085013567ffffffffffffffff8082111561182e57600080fd5b818701915087601f83011261184257600080fd5b81358181111561185157600080fd5b88602082850101111561186357600080fd5b95989497505060200194505050565b6020808252602e908201527f54656c65706f727465725570677261646561626c653a207a65726f2054656c6560408201526d706f72746572206164647265737360901b606082015260800190565b60018060a01b03851681528360208201528260408201526080606082015260006104de6080830184611770565b634e487b7160e01b600052603260045260246000fd5b602081526000610c4c6020830184611744565b634e487b7160e01b600052601160045260246000fd5b60006001820161193e5761193e611916565b5060010190565b600181811c9082168061195957607f821691505b60208210810361197957634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561199157600080fd5b5051919050565b81810381811115610b2757610b27611916565b600081518084526020808501945080840160005b838110156119e45781516001600160a01b0316875295820195908201906001016119bf565b509495945050505050565b60208152815160208201526000602083015160018060a01b03808216604085015260408501519150808251166060850152506020810151608084015250606083015160a0830152608083015160e060c0840152611a506101008401826119ab565b905060a0840151601f198483030160e0850152610b228282611744565b60006020808385031215611a8057600080fd5b825167ffffffffffffffff80821115611a9857600080fd5b818501915085601f830112611aac57600080fd5b8151611aba6116078261152c565b81815260059190911b83018401908481019088831115611ad957600080fd5b8585015b83811015611b4d57805185811115611af55760008081fd5b8601603f81018b13611b075760008081fd5b878101516040611b1961160783611550565b8281528d82848601011115611b2e5760008081fd5b611b3d838c8301848701611720565b8652505050918601918601611add565b5098975050505050505050565b602081526000610c4c6020830184611770565b600060208284031215611b7f57600080fd5b8151610c4c816114b3565b80820180821115610b2757610b27611916565b600060208284031215611baf57600080fd5b81518015158114610c4c57600080fd5b60008251611bd1818460208701611720565b9190910192915050565b601f82111561126157600081815260208120601f850160051c81016020861015611c025750805b601f850160051c820191505b81811015611c2157828155600101611c0e565b505050505050565b815167ffffffffffffffff811115611c4357611c436114e5565b611c5781611c518454611945565b84611bdb565b602080601f831160018114611c8c5760008415611c745750858301515b600019600386901b1c1916600185901b178555611c21565b600085815260208120601f198616915b82811015611cbb57888601518255948401946001909101908401611c9c565b5085821015611cd95787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea264697066735822122099c1934ed6c29efc8a8f1a00000c54700422fb3a36d744d19eddd761d3af6f2c64736f6c63430008120033", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterRegistryAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"teleporterManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"oldMinTeleporterVersion\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"newMinTeleporterVersion\",\"type\":\"uint256\"}],\"name\":\"MinTeleporterVersionUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"originSenderAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"ReceiveMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"destinationBlockchainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"feeTokenAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string[]\",\"name\":\"messages\",\"type\":\"string[]\"}],\"name\":\"SendMessages\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"TeleporterAddressPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"TeleporterAddressUnpaused\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"}],\"name\":\"getCurrentMessages\",\"outputs\":[{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMinTeleporterVersion\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"isTeleporterAddressPaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"pauseTeleporterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"sourceBlockchainID\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"originSenderAddress\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"receiveTeleporterMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationBlockchainID\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeTokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"string[]\",\"name\":\"messages\",\"type\":\"string[]\"}],\"name\":\"sendMessages\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"teleporterRegistry\",\"outputs\":[{\"internalType\":\"contractTeleporterRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"teleporterAddress\",\"type\":\"address\"}],\"name\":\"unpauseTeleporterAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"version\",\"type\":\"uint256\"}],\"name\":\"updateMinTeleporterVersion\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b5060405162001e7938038062001e7983398101604081905262000034916200029f565b60016000558181816001600160a01b038116620000be5760405162461bcd60e51b815260206004820152603760248201527f54656c65706f727465725570677261646561626c653a207a65726f2074656c6560448201527f706f72746572207265676973747279206164647265737300000000000000000060648201526084015b60405180910390fd5b6001600160a01b03811660808190526040805163301fd1f560e21b8152905163c07f47d4916004808201926020929091908290030181865afa15801562000109573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200012f9190620002d7565b600255506200013e3362000153565b6200014981620001a5565b50505050620002f1565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b620001af62000224565b6001600160a01b038116620002165760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401620000b5565b620002218162000153565b50565b6003546001600160a01b03163314620002805760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620000b5565b565b80516001600160a01b03811681146200029a57600080fd5b919050565b60008060408385031215620002b357600080fd5b620002be8362000282565b9150620002ce6020840162000282565b90509250929050565b600060208284031215620002ea57600080fd5b5051919050565b608051611b58620003216000396000818160be0152818161070401528181610c240152610f660152611b586000f3fe608060405234801561001057600080fd5b50600436106100b45760003560e01c80638da5cb5b116100715780638da5cb5b146101605780639731429714610171578063c1329fcb146101ad578063c868efaa146101cd578063d2cc7a70146101e0578063f2fde38b146101f157600080fd5b80631a7f5bec146100b95780632b0d8f18146100fd5780633902970c146101125780634511243e146101325780635eb9951414610145578063715018a614610158575b600080fd5b6100e07f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61011061010b3660046113a3565b610204565b005b61012561012036600461142f565b610309565b6040516100f49190611596565b6101106101403660046113a3565b6104e7565b6101106101533660046115da565b6105e4565b6101106105f8565b6003546001600160a01b03166100e0565b61019d61017f3660046113a3565b6001600160a01b031660009081526001602052604090205460ff1690565b60405190151581526020016100f4565b6101c06101bb3660046115da565b61060c565b6040516100f49190611698565b6101106101db3660046116ab565b6106ef565b6002546040519081526020016100f4565b6101106101ff3660046113a3565b6108b9565b61020c61092f565b6001600160a01b03811661023b5760405162461bcd60e51b815260040161023290611734565b60405180910390fd5b6001600160a01b03811660009081526001602052604090205460ff16156102ba5760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a2061646472657373206160448201526c1b1c9958591e481c185d5cd959609a1b6064820152608401610232565b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f933f93e57a222e6330362af8b376d0a8725b6901e9a2fb86d00f169702b28a4c9190a250565b6060610313610937565b60008415610328576103258686610990565b90505b866001600160a01b0316887f430d1906813fdb2129a19139f4112a1396804605501a798df3a4042590ba20d5888488886040516103689493929190611782565b60405180910390a36000835167ffffffffffffffff81111561038c5761038c6113c0565b6040519080825280602002602001820160405280156103b5578160200160208202803683370190505b50905060005b84518110156104cf57600061049c6040518060c001604052808d81526020018c6001600160a01b0316815260200160405180604001604052808d6001600160a01b03168152602001888152508152602001898152602001600067ffffffffffffffff81111561042c5761042c6113c0565b604051908082528060200260200182016040528015610455578160200160208202803683370190505b50815260200188858151811061046d5761046d6117af565b602002602001015160405160200161048591906117c5565b604051602081830303815290604052815250610afa565b9050808383815181106104b1576104b16117af565b602090810291909101015250806104c7816117ee565b9150506103bb565b509150506104dd6001600055565b9695505050505050565b6104ef61092f565b6001600160a01b0381166105155760405162461bcd60e51b815260040161023290611734565b6001600160a01b03811660009081526001602052604090205460ff1661058f5760405162461bcd60e51b815260206004820152602960248201527f54656c65706f727465725570677261646561626c653a2061646472657373206e6044820152681bdd081c185d5cd95960ba1b6064820152608401610232565b6040516001600160a01b038216907f844e2f3154214672229235858fd029d1dfd543901c6d05931f0bc2480a2d72c390600090a26001600160a01b03166000908152600160205260409020805460ff19169055565b6105ec61092f565b6105f581610c20565b50565b610600610dc0565b61060a6000610e1a565b565b6000818152600460209081526040808320805482518185028101850190935280835260609493849084015b828210156106e357838290600052602060002001805461065690611807565b80601f016020809104026020016040519081016040528092919081815260200182805461068290611807565b80156106cf5780601f106106a4576101008083540402835291602001916106cf565b820191906000526020600020905b8154815290600101906020018083116106b257829003601f168201915b505050505081526020019060010190610637565b50929695505050505050565b6106f7610937565b6002546001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016634c1f08ce336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa15801561076e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107929190611841565b10156107f95760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201526f32b632b837b93a32b91039b2b73232b960811b6064820152608401610232565b6108023361017f565b156108685760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881859191c995cdcc81c185d5cd95960821b6064820152608401610232565b6108a9848484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e6c92505050565b6108b36001600055565b50505050565b6108c1610dc0565b6001600160a01b0381166109265760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610232565b6105f581610e1a565b61060a610dc0565b6002600054036109895760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610232565b6002600055565b6040516370a0823160e01b815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa1580156109d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109fd9190611841565b9050610a146001600160a01b038516333086610ef6565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa158015610a5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a7f9190611841565b9050818111610ae55760405162461bcd60e51b815260206004820152602c60248201527f5361666545524332305472616e7366657246726f6d3a2062616c616e6365206e60448201526b1bdd081a5b98dc99585cd95960a21b6064820152608401610232565b610aef828261185a565b925050505b92915050565b600080610b05610f61565b60408401516020015190915015610baa576040830151516001600160a01b0316610b875760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a207a65726f206665652060448201526c746f6b656e206164647265737360981b6064820152608401610232565b604083015160208101519051610baa916001600160a01b03909116908390611075565b604051630624488560e41b81526001600160a01b03821690636244885090610bd69086906004016118b1565b6020604051808303816000875af1158015610bf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c199190611841565b9392505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca49190611841565b60025490915081831115610d145760405162461bcd60e51b815260206004820152603160248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201527032b632b837b93a32b9103b32b939b4b7b760791b6064820152608401610232565b808311610d895760405162461bcd60e51b815260206004820152603f60248201527f54656c65706f727465725570677261646561626c653a206e6f7420677265617460448201527f6572207468616e2063757272656e74206d696e696d756d2076657273696f6e006064820152608401610232565b6002839055604051839082907fa9a7ef57e41f05b4c15480842f5f0c27edfcbb553fed281f7c4068452cc1c02d90600090a3505050565b6003546001600160a01b0316331461060a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610232565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600081806020019051810190610e82919061192f565b600085815260046020908152604082208054600181018255908352912091925001610ead82826119f4565b50826001600160a01b0316847f1f5c800b5f2b573929a7948f82a199c2a212851b53a6c5bd703ece23999d24aa83604051610ee891906117c5565b60405180910390a350505050565b6040516001600160a01b03808516602483015283166044820152606481018290526108b39085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611127565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d820e64f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe69190611ab4565b905061100a816001600160a01b031660009081526001602052604090205460ff1690565b156110705760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881cd95b991a5b99c81c185d5cd95960821b6064820152608401610232565b919050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156110c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ea9190611841565b6110f49190611ad1565b6040516001600160a01b0385166024820152604481018290529091506108b390859063095ea7b360e01b90606401610f2a565b600061117c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166111fe9092919063ffffffff16565b8051909150156111f9578080602001905181019061119a9190611ae4565b6111f95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610232565b505050565b606061120d8484600085611215565b949350505050565b6060824710156112765760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610232565b600080866001600160a01b031685876040516112929190611b06565b60006040518083038185875af1925050503d80600081146112cf576040519150601f19603f3d011682016040523d82523d6000602084013e6112d4565b606091505b50915091506112e5878383876112f0565b979650505050505050565b6060831561135f578251600003611358576001600160a01b0385163b6113585760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610232565b508161120d565b61120d83838151156113745781518083602001fd5b8060405162461bcd60e51b815260040161023291906117c5565b6001600160a01b03811681146105f557600080fd5b6000602082840312156113b557600080fd5b8135610c198161138e565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156113ff576113ff6113c0565b604052919050565b600067ffffffffffffffff821115611421576114216113c0565b50601f01601f191660200190565b60008060008060008060c0878903121561144857600080fd5b86359550611459602088013561138e565b6020870135945061146d604088013561138e565b60408701359350606087013592506080870135915067ffffffffffffffff60a0880135111561149b57600080fd5b60a0870135870188601f8201126114b157600080fd5b67ffffffffffffffff813511156114ca576114ca6113c0565b6114da6020823560051b016113d6565b81358082526020808301929160051b8401018b8111156114f957600080fd5b602084015b818110156115845767ffffffffffffffff8135111561151c57600080fd5b803585018d603f82011261152f57600080fd5b602081013561154561154082611407565b6113d6565b8181528f604083850101111561155a57600080fd5b816040840160208301376000602083830101528087525050506020840193506020810190506114fe565b50508093505050509295509295509295565b6020808252825182820181905260009190848201906040850190845b818110156115ce578351835292840192918401916001016115b2565b50909695505050505050565b6000602082840312156115ec57600080fd5b5035919050565b60005b8381101561160e5781810151838201526020016115f6565b50506000910152565b6000815180845261162f8160208601602086016115f3565b601f01601f19169290920160200192915050565b600081518084526020808501808196508360051b8101915082860160005b8581101561168b578284038952611679848351611617565b98850198935090840190600101611661565b5091979650505050505050565b602081526000610c196020830184611643565b600080600080606085870312156116c157600080fd5b8435935060208501356116d38161138e565b9250604085013567ffffffffffffffff808211156116f057600080fd5b818701915087601f83011261170457600080fd5b81358181111561171357600080fd5b88602082850101111561172557600080fd5b95989497505060200194505050565b6020808252602e908201527f54656c65706f727465725570677261646561626c653a207a65726f2054656c6560408201526d706f72746572206164647265737360901b606082015260800190565b60018060a01b03851681528360208201528260408201526080606082015260006104dd6080830184611643565b634e487b7160e01b600052603260045260246000fd5b602081526000610c196020830184611617565b634e487b7160e01b600052601160045260246000fd5b600060018201611800576118006117d8565b5060010190565b600181811c9082168061181b57607f821691505b60208210810361183b57634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561185357600080fd5b5051919050565b81810381811115610af457610af46117d8565b600081518084526020808501945080840160005b838110156118a65781516001600160a01b031687529582019590820190600101611881565b509495945050505050565b60208152815160208201526000602083015160018060a01b03808216604085015260408501519150808251166060850152506020810151608084015250606083015160a0830152608083015160e060c084015261191261010084018261186d565b905060a0840151601f198483030160e0850152610aef8282611617565b60006020828403121561194157600080fd5b815167ffffffffffffffff81111561195857600080fd5b8201601f8101841361196957600080fd5b805161197761154082611407565b81815285602083850101111561198c57600080fd5b61199d8260208301602086016115f3565b95945050505050565b601f8211156111f957600081815260208120601f850160051c810160208610156119cd5750805b601f850160051c820191505b818110156119ec578281556001016119d9565b505050505050565b815167ffffffffffffffff811115611a0e57611a0e6113c0565b611a2281611a1c8454611807565b846119a6565b602080601f831160018114611a575760008415611a3f5750858301515b600019600386901b1c1916600185901b1785556119ec565b600085815260208120601f198616915b82811015611a8657888601518255948401946001909101908401611a67565b5085821015611aa45787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215611ac657600080fd5b8151610c198161138e565b80820180821115610af457610af46117d8565b600060208284031215611af657600080fd5b81518015158114610c1957600080fd5b60008251611b188184602087016115f3565b919091019291505056fea2646970667358221220257a55014d2e2efb8773812d91e07b56e09162796771c80a372b9e2533ff794f64736f6c63430008120033", } // BatchCrossChainMessengerABI is the input ABI used to generate the binding from. @@ -204,33 +204,32 @@ func (_BatchCrossChainMessenger *BatchCrossChainMessengerTransactorRaw) Transact // GetCurrentMessages is a free data retrieval call binding the contract method 0xc1329fcb. // -// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(address, string[]) -func (_BatchCrossChainMessenger *BatchCrossChainMessengerCaller) GetCurrentMessages(opts *bind.CallOpts, sourceBlockchainID [32]byte) (common.Address, []string, error) { +// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(string[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCaller) GetCurrentMessages(opts *bind.CallOpts, sourceBlockchainID [32]byte) ([]string, error) { var out []interface{} err := _BatchCrossChainMessenger.contract.Call(opts, &out, "getCurrentMessages", sourceBlockchainID) if err != nil { - return *new(common.Address), *new([]string), err + return *new([]string), err } - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - out1 := *abi.ConvertType(out[1], new([]string)).(*[]string) + out0 := *abi.ConvertType(out[0], new([]string)).(*[]string) - return out0, out1, err + return out0, err } // GetCurrentMessages is a free data retrieval call binding the contract method 0xc1329fcb. // -// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(address, string[]) -func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) GetCurrentMessages(sourceBlockchainID [32]byte) (common.Address, []string, error) { +// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(string[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerSession) GetCurrentMessages(sourceBlockchainID [32]byte) ([]string, error) { return _BatchCrossChainMessenger.Contract.GetCurrentMessages(&_BatchCrossChainMessenger.CallOpts, sourceBlockchainID) } // GetCurrentMessages is a free data retrieval call binding the contract method 0xc1329fcb. // -// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(address, string[]) -func (_BatchCrossChainMessenger *BatchCrossChainMessengerCallerSession) GetCurrentMessages(sourceBlockchainID [32]byte) (common.Address, []string, error) { +// Solidity: function getCurrentMessages(bytes32 sourceBlockchainID) view returns(string[]) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerCallerSession) GetCurrentMessages(sourceBlockchainID [32]byte) ([]string, error) { return _BatchCrossChainMessenger.Contract.GetCurrentMessages(&_BatchCrossChainMessenger.CallOpts, sourceBlockchainID) } @@ -811,9 +810,9 @@ func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseOwnershi return event, nil } -// BatchCrossChainMessengerReceiveMessagesIterator is returned from FilterReceiveMessages and is used to iterate over the raw logs and unpacked data for ReceiveMessages events raised by the BatchCrossChainMessenger contract. -type BatchCrossChainMessengerReceiveMessagesIterator struct { - Event *BatchCrossChainMessengerReceiveMessages // Event containing the contract specifics and raw log +// BatchCrossChainMessengerReceiveMessageIterator is returned from FilterReceiveMessage and is used to iterate over the raw logs and unpacked data for ReceiveMessage events raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerReceiveMessageIterator struct { + Event *BatchCrossChainMessengerReceiveMessage // Event containing the contract specifics and raw log contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data @@ -827,7 +826,7 @@ type BatchCrossChainMessengerReceiveMessagesIterator struct { // Next advances the iterator to the subsequent event, returning whether there // are any more events found. In case of a retrieval or parsing error, false is // returned and Error() can be queried for the exact failure. -func (it *BatchCrossChainMessengerReceiveMessagesIterator) Next() bool { +func (it *BatchCrossChainMessengerReceiveMessageIterator) Next() bool { // If the iterator failed, stop iterating if it.fail != nil { return false @@ -836,7 +835,7 @@ func (it *BatchCrossChainMessengerReceiveMessagesIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(BatchCrossChainMessengerReceiveMessages) + it.Event = new(BatchCrossChainMessengerReceiveMessage) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -851,7 +850,7 @@ func (it *BatchCrossChainMessengerReceiveMessagesIterator) Next() bool { // Iterator still in progress, wait for either a data or an error event select { case log := <-it.logs: - it.Event = new(BatchCrossChainMessengerReceiveMessages) + it.Event = new(BatchCrossChainMessengerReceiveMessage) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -867,29 +866,29 @@ func (it *BatchCrossChainMessengerReceiveMessagesIterator) Next() bool { } // Error returns any retrieval or parsing error occurred during filtering. -func (it *BatchCrossChainMessengerReceiveMessagesIterator) Error() error { +func (it *BatchCrossChainMessengerReceiveMessageIterator) Error() error { return it.fail } // Close terminates the iteration process, releasing any pending underlying // resources. -func (it *BatchCrossChainMessengerReceiveMessagesIterator) Close() error { +func (it *BatchCrossChainMessengerReceiveMessageIterator) Close() error { it.sub.Unsubscribe() return nil } -// BatchCrossChainMessengerReceiveMessages represents a ReceiveMessages event raised by the BatchCrossChainMessenger contract. -type BatchCrossChainMessengerReceiveMessages struct { +// BatchCrossChainMessengerReceiveMessage represents a ReceiveMessage event raised by the BatchCrossChainMessenger contract. +type BatchCrossChainMessengerReceiveMessage struct { SourceBlockchainID [32]byte OriginSenderAddress common.Address - Messages []string + Message string Raw types.Log // Blockchain specific contextual infos } -// FilterReceiveMessages is a free log retrieval operation binding the contract event 0x903a123a8d947dcbd005d05b20bb5180f878c6d3527681c1abb1d8987714cb9d. +// FilterReceiveMessage is a free log retrieval operation binding the contract event 0x1f5c800b5f2b573929a7948f82a199c2a212851b53a6c5bd703ece23999d24aa. // -// Solidity: event ReceiveMessages(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string[] messages) -func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterReceiveMessages(opts *bind.FilterOpts, sourceBlockchainID [][32]byte, originSenderAddress []common.Address) (*BatchCrossChainMessengerReceiveMessagesIterator, error) { +// Solidity: event ReceiveMessage(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string message) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterReceiveMessage(opts *bind.FilterOpts, sourceBlockchainID [][32]byte, originSenderAddress []common.Address) (*BatchCrossChainMessengerReceiveMessageIterator, error) { var sourceBlockchainIDRule []interface{} for _, sourceBlockchainIDItem := range sourceBlockchainID { @@ -900,17 +899,17 @@ func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) FilterReceive originSenderAddressRule = append(originSenderAddressRule, originSenderAddressItem) } - logs, sub, err := _BatchCrossChainMessenger.contract.FilterLogs(opts, "ReceiveMessages", sourceBlockchainIDRule, originSenderAddressRule) + logs, sub, err := _BatchCrossChainMessenger.contract.FilterLogs(opts, "ReceiveMessage", sourceBlockchainIDRule, originSenderAddressRule) if err != nil { return nil, err } - return &BatchCrossChainMessengerReceiveMessagesIterator{contract: _BatchCrossChainMessenger.contract, event: "ReceiveMessages", logs: logs, sub: sub}, nil + return &BatchCrossChainMessengerReceiveMessageIterator{contract: _BatchCrossChainMessenger.contract, event: "ReceiveMessage", logs: logs, sub: sub}, nil } -// WatchReceiveMessages is a free log subscription operation binding the contract event 0x903a123a8d947dcbd005d05b20bb5180f878c6d3527681c1abb1d8987714cb9d. +// WatchReceiveMessage is a free log subscription operation binding the contract event 0x1f5c800b5f2b573929a7948f82a199c2a212851b53a6c5bd703ece23999d24aa. // -// Solidity: event ReceiveMessages(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string[] messages) -func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchReceiveMessages(opts *bind.WatchOpts, sink chan<- *BatchCrossChainMessengerReceiveMessages, sourceBlockchainID [][32]byte, originSenderAddress []common.Address) (event.Subscription, error) { +// Solidity: event ReceiveMessage(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string message) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchReceiveMessage(opts *bind.WatchOpts, sink chan<- *BatchCrossChainMessengerReceiveMessage, sourceBlockchainID [][32]byte, originSenderAddress []common.Address) (event.Subscription, error) { var sourceBlockchainIDRule []interface{} for _, sourceBlockchainIDItem := range sourceBlockchainID { @@ -921,7 +920,7 @@ func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchReceiveM originSenderAddressRule = append(originSenderAddressRule, originSenderAddressItem) } - logs, sub, err := _BatchCrossChainMessenger.contract.WatchLogs(opts, "ReceiveMessages", sourceBlockchainIDRule, originSenderAddressRule) + logs, sub, err := _BatchCrossChainMessenger.contract.WatchLogs(opts, "ReceiveMessage", sourceBlockchainIDRule, originSenderAddressRule) if err != nil { return nil, err } @@ -931,8 +930,8 @@ func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchReceiveM select { case log := <-logs: // New log arrived, parse the event and forward to the user - event := new(BatchCrossChainMessengerReceiveMessages) - if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "ReceiveMessages", log); err != nil { + event := new(BatchCrossChainMessengerReceiveMessage) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "ReceiveMessage", log); err != nil { return err } event.Raw = log @@ -953,12 +952,12 @@ func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) WatchReceiveM }), nil } -// ParseReceiveMessages is a log parse operation binding the contract event 0x903a123a8d947dcbd005d05b20bb5180f878c6d3527681c1abb1d8987714cb9d. +// ParseReceiveMessage is a log parse operation binding the contract event 0x1f5c800b5f2b573929a7948f82a199c2a212851b53a6c5bd703ece23999d24aa. // -// Solidity: event ReceiveMessages(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string[] messages) -func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseReceiveMessages(log types.Log) (*BatchCrossChainMessengerReceiveMessages, error) { - event := new(BatchCrossChainMessengerReceiveMessages) - if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "ReceiveMessages", log); err != nil { +// Solidity: event ReceiveMessage(bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string message) +func (_BatchCrossChainMessenger *BatchCrossChainMessengerFilterer) ParseReceiveMessage(log types.Log) (*BatchCrossChainMessengerReceiveMessage, error) { + event := new(BatchCrossChainMessengerReceiveMessage) + if err := _BatchCrossChainMessenger.contract.UnpackLog(event, "ReceiveMessage", log); err != nil { return nil, err } event.Raw = log diff --git a/tests/batch_relay.go b/tests/batch_relay.go index 621de946..0f81501b 100644 --- a/tests/batch_relay.go +++ b/tests/batch_relay.go @@ -2,7 +2,9 @@ package tests import ( "context" + "fmt" "math/big" + "strconv" "time" "github.com/ava-labs/avalanchego/utils/set" @@ -81,10 +83,10 @@ func BatchRelay(network interfaces.LocalNetwork) { Expect(err).Should(BeNil()) defer sub.Unsubscribe() - messages := []string{"hello", "world", "this", "is", "a", "cross", "chain", "batch", "message"} - sentMessages := set.NewSet[string](len(messages)) - for _, msg := range messages { - sentMessages.Add(msg) + numMessages := 50 + sentMessages := set.NewSet[string](numMessages) + for i := range numMessages { + sentMessages.Add(strconv.Itoa(i)) } optsA, err := bind.NewKeyedTransactorWithChainID(fundedKey, subnetAInfo.EVMChainID) @@ -95,19 +97,30 @@ func BatchRelay(network interfaces.LocalNetwork) { batchMessengerAddressB, common.Address{}, big.NewInt(0), - big.NewInt(500000), - messages, + big.NewInt(int64(300000*numMessages)), + sentMessages.List(), ) Expect(err).Should(BeNil()) utils.WaitForTransactionSuccess(ctx, subnetAInfo, tx.Hash()) // Wait for the message on the destination - <-newHeadsDest - - _, receivedMessages, err := batchMessengerB.GetCurrentMessages(&bind.CallOpts{}, subnetAInfo.BlockchainID) - Expect(err).Should(BeNil()) - for _, msg := range receivedMessages { - Expect(sentMessages.Contains(msg)).To(BeTrue()) + maxWait := 30 + currWait := 0 + log.Info("Waiting to receive all messages on destination...") + for { + receivedMessages, err := batchMessengerB.GetCurrentMessages(&bind.CallOpts{}, subnetAInfo.BlockchainID) + Expect(err).Should(BeNil()) + + // Remove the received messages from the set of sent messages + sentMessages.Remove(receivedMessages...) + if sentMessages.Len() == 0 { + break + } + 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)) + } + time.Sleep(1 * time.Second) } } diff --git a/tests/contracts/src/BatchCrossChainMessenger.sol b/tests/contracts/src/BatchCrossChainMessenger.sol index eea360cf..33b0f471 100644 --- a/tests/contracts/src/BatchCrossChainMessenger.sol +++ b/tests/contracts/src/BatchCrossChainMessenger.sol @@ -28,7 +28,7 @@ contract BatchCrossChainMessenger is ReentrancyGuard, TeleporterOwnerUpgradeable string[] messages; } - mapping(bytes32 sourceBlockchainID => Messages messages) private _messages; + mapping(bytes32 sourceBlockchainID => string[] messages) private _messages; /** * @dev Emitted when a message is submited to be sent. @@ -45,8 +45,8 @@ contract BatchCrossChainMessenger is ReentrancyGuard, TeleporterOwnerUpgradeable /** * @dev Emitted when a new message is received from a given chain ID. */ - event ReceiveMessages( - bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string[] messages + event ReceiveMessage( + bytes32 indexed sourceBlockchainID, address indexed originSenderAddress, string message ); constructor( @@ -105,10 +105,10 @@ contract BatchCrossChainMessenger is ReentrancyGuard, TeleporterOwnerUpgradeable function getCurrentMessages(bytes32 sourceBlockchainID) external view - returns (address, string[] memory) + returns (string[] memory) { - Messages memory messageInfo = _messages[sourceBlockchainID]; - return (messageInfo.sender, messageInfo.messages); + string[] memory messages = _messages[sourceBlockchainID]; + return messages; } /** @@ -119,11 +119,11 @@ contract BatchCrossChainMessenger is ReentrancyGuard, TeleporterOwnerUpgradeable function _receiveTeleporterMessage( bytes32 sourceBlockchainID, address originSenderAddress, - bytes memory messages + bytes memory message ) internal override { // Store the message. - string[] memory messagesString = abi.decode(messages, (string[])); - _messages[sourceBlockchainID] = Messages(originSenderAddress, messagesString); - emit ReceiveMessages(sourceBlockchainID, originSenderAddress, messagesString); + string memory messageString = abi.decode(message, (string)); + _messages[sourceBlockchainID].push(messageString); + emit ReceiveMessage(sourceBlockchainID, originSenderAddress, messageString); } } From 8dce7b4f221e48f8df71086896870fda4cac5dda Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Wed, 15 May 2024 09:53:05 -0500 Subject: [PATCH 29/49] comments+cleanup --- main/main.go | 23 +++++++++++++++-------- peers/app_request_network.go | 14 +++++++------- peers/external_handler.go | 5 ++++- types/types.go | 10 +++++----- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/main/main.go b/main/main.go index 94e52d73..95c8f24f 100644 --- a/main/main.go +++ b/main/main.go @@ -178,6 +178,7 @@ func main() { ticker := utils.NewTicker(cfg.DBWriteIntervalSeconds) go ticker.Run() + // Gather manual Warp messages specified in the configuration manualWarpMessages := make(map[ids.ID][]*relayerTypes.WarpMessageInfo) for _, msg := range cfg.ManualWarpMessages { sourceBlockchainID := msg.GetSourceBlockchainID() @@ -196,7 +197,7 @@ func main() { manualWarpMessages[sourceBlockchainID] = append(manualWarpMessages[sourceBlockchainID], &warpLogInfo) } - // Create relayers for each of the subnets configured as a source + // 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) @@ -214,8 +215,8 @@ func main() { // errgroup will cancel the context when the first goroutine returns an error errGroup.Go(func() error { - // runRelayer runs until it errors or the context is cancelled by another goroutine - return runRelayer( + // runListener runs until it errors or the context is cancelled by another goroutine + return runListener( ctx, logger, metrics, @@ -238,8 +239,9 @@ func main() { ) } -// runRelayer creates a relayer instance for a subnet. It listens for warp messages on that subnet, and handles delivery to the destination -func runRelayer( +// 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, metrics *relayer.ApplicationRelayerMetrics, @@ -253,11 +255,13 @@ func runRelayer( manualWarpMessages []*relayerTypes.WarpMessageInfo, cfg *config.Config, ) error { - // Create the application relayers + // Create the ApplicationRelayers logger.Info( "Creating application relayers", zap.String("originBlockchainID", sourceBlockchain.BlockchainID), ) + applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) + ethClient, err := ethclient.DialWithConfig( context.Background(), sourceBlockchain.RPCEndpoint.BaseURL, @@ -272,6 +276,7 @@ func runRelayer( ) return err } + currentHeight, err := ethClient.BlockNumber(context.Background()) if err != nil { logger.Error( @@ -280,9 +285,10 @@ func runRelayer( ) return err } - applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) - minHeight := uint64(0) + // Each ApplicationRelayer determines its starting height based on the database state. + // The Listener back-processes from the minimum height across all of the ApplicationRelayers. + minHeight := uint64(0) for _, relayerID := range database.GetSourceBlockchainRelayerIDs(&sourceBlockchain) { height, err := database.CalculateStartingBlockHeight( logger, @@ -325,6 +331,7 @@ func runRelayer( applicationRelayers[relayerID.ID] = applicationRelayer } + // Create the Listener logger.Info( "Creating listener", zap.String("originBlockchainID", sourceBlockchain.BlockchainID), diff --git a/peers/app_request_network.go b/peers/app_request_network.go index d51d06c2..8a915cde 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -39,7 +39,7 @@ type AppRequestNetwork struct { validatorClient *validators.CanonicalValidatorClient } -// NewNetwork connects to a peers at the app request level. +// NewNetwork creates a p2p network client for interacting with validators func NewNetwork( logLevel logging.Level, registerer prometheus.Registerer, @@ -54,12 +54,7 @@ func NewNetwork( ), ) - // Create the test network for AppRequests - var trackedSubnets set.Set[ids.ID] - for _, sourceBlockchain := range cfg.SourceBlockchains { - trackedSubnets.Add(sourceBlockchain.GetSubnetID()) - } - + // Create the handler for handling inbound app responses handler, err := NewRelayerExternalHandler(logger, registerer) if err != nil { logger.Error( @@ -86,6 +81,11 @@ func NewNetwork( return nil, err } + var trackedSubnets set.Set[ids.ID] + for _, sourceBlockchain := range cfg.SourceBlockchains { + trackedSubnets.Add(sourceBlockchain.GetSubnetID()) + } + testNetwork, err := network.NewTestNetwork(logger, networkID, snowVdrs.NewManager(), trackedSubnets, handler) if err != nil { logger.Error( diff --git a/peers/external_handler.go b/peers/external_handler.go index 5e8d5076..36e5b51f 100644 --- a/peers/external_handler.go +++ b/peers/external_handler.go @@ -32,6 +32,7 @@ type RelayerExternalHandler struct { timeoutManager timer.AdaptiveTimeoutManager } +// expectedResponses counts the number of responses and compares against the expected number of responses type expectedResponses struct { expected, received int } @@ -108,6 +109,8 @@ 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 { // Create a channel to receive the response h.responseChansLock.Lock() @@ -188,7 +191,7 @@ func (h *RelayerExternalHandler) RegisterAppResponse(inboundMessage message.Inbo h.log.Debug("Could not find response channel for request", zap.Uint32("requestID", requestID)) } - // Check for the expected number of responses + // Check for the expected number of responses, and clear from the map if all expected responses have been received responses := h.responsesCount[reqID.RequestID] received := responses.received + 1 if received == responses.expected { diff --git a/types/types.go b/types/types.go index 3393f5ed..4a7ca793 100644 --- a/types/types.go +++ b/types/types.go @@ -22,16 +22,16 @@ 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 +// Listener to process type WarpBlockInfo struct { BlockNumber uint64 Messages []*WarpMessageInfo } -// NewWarpMessageInfo describes the transaction information for the Warp message -// sent on the source chain, and includes the Warp Message payload bytes -// NewWarpMessageInfo instances are either derived from the logs of a block or -// from the manual Warp message information provided via configuration +// WarpMessageInfo describes the transaction information for the Warp message +// sent on the source chain. +// WarpMessageInfo instances are either derived from the logs of a block or +// from the manual Warp message information provided via configuration. type WarpMessageInfo struct { SourceAddress common.Address UnsignedMessage *avalancheWarp.UnsignedMessage From f1887c958a77198780e13c203bfe27ccc4d22b90 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 21 May 2024 10:32:22 -0500 Subject: [PATCH 30/49] cleanup external handler --- peers/external_handler.go | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/peers/external_handler.go b/peers/external_handler.go index 36e5b51f..9ef7a077 100644 --- a/peers/external_handler.go +++ b/peers/external_handler.go @@ -25,11 +25,11 @@ var _ router.ExternalHandler = &RelayerExternalHandler{} // is possible for multiple concurrent calls to happen with different NodeIDs. // However, a given NodeID will only be performing one call at a time. type RelayerExternalHandler struct { - log logging.Logger - responseChansLock *sync.RWMutex - responseChans map[uint32]chan message.InboundMessage - responsesCount map[uint32]expectedResponses - timeoutManager timer.AdaptiveTimeoutManager + log logging.Logger + lock *sync.Mutex + responseChans map[uint32]chan message.InboundMessage + responsesCount map[uint32]expectedResponses + timeoutManager timer.AdaptiveTimeoutManager } // expectedResponses counts the number of responses and compares against the expected number of responses @@ -63,11 +63,11 @@ func NewRelayerExternalHandler( go timeoutManager.Dispatch() return &RelayerExternalHandler{ - log: logger, - responseChansLock: &sync.RWMutex{}, - responseChans: make(map[uint32]chan message.InboundMessage), - responsesCount: make(map[uint32]expectedResponses), - timeoutManager: timeoutManager, + log: logger, + lock: &sync.Mutex{}, + responseChans: make(map[uint32]chan message.InboundMessage), + responsesCount: make(map[uint32]expectedResponses), + timeoutManager: timeoutManager, }, nil } @@ -86,7 +86,7 @@ func (h *RelayerExternalHandler) HandleInbound(_ context.Context, inboundMessage zap.Stringer("from", inboundMessage.NodeID()), ) if inboundMessage.Op() == message.AppResponseOp || inboundMessage.Op() == message.AppErrorOp { - h.RegisterAppResponse(inboundMessage) + h.registerAppResponse(inboundMessage) } else { h.log.Debug("Ignoring message", zap.Stringer("op", inboundMessage.Op())) inboundMessage.OnFinishedHandling() @@ -113,8 +113,8 @@ func (h *RelayerExternalHandler) Disconnected(nodeID ids.NodeID) { // 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.responseChansLock.Lock() - defer h.responseChansLock.Unlock() + h.lock.Lock() + defer h.lock.Unlock() h.log.Debug("Registering request ID", zap.Uint32("requestID", requestID)) @@ -143,9 +143,9 @@ func (h *RelayerExternalHandler) RegisterAppRequest(reqID ids.RequestID) { } // RegisterResponse registers an AppResponse with the timeout manager -func (h *RelayerExternalHandler) RegisterAppResponse(inboundMessage message.InboundMessage) { - h.responseChansLock.Lock() - defer h.responseChansLock.Unlock() +func (h *RelayerExternalHandler) registerAppResponse(inboundMessage message.InboundMessage) { + h.lock.Lock() + defer h.lock.Unlock() // Extract the message fields m := inboundMessage.Message() @@ -173,6 +173,7 @@ func (h *RelayerExternalHandler) RegisterAppResponse(inboundMessage message.Inbo return } + // Remove the timeout on the request reqID := ids.RequestID{ NodeID: inboundMessage.NodeID(), SourceChainID: sourceBlockchainID, @@ -180,8 +181,6 @@ func (h *RelayerExternalHandler) RegisterAppResponse(inboundMessage message.Inbo RequestID: requestID, Op: byte(inboundMessage.Op()), } - - // Register the response with the timeout manager h.timeoutManager.Remove(reqID) // Dispatch to the appropriate response channel @@ -189,18 +188,19 @@ func (h *RelayerExternalHandler) RegisterAppResponse(inboundMessage message.Inbo responseChan <- inboundMessage } else { h.log.Debug("Could not find response channel for request", zap.Uint32("requestID", requestID)) + return } // Check for the expected number of responses, and clear from the map if all expected responses have been received - responses := h.responsesCount[reqID.RequestID] + responses := h.responsesCount[requestID] received := responses.received + 1 if received == responses.expected { - close(h.responseChans[reqID.RequestID]) - delete(h.responseChans, reqID.RequestID) - delete(h.responsesCount, reqID.RequestID) + close(h.responseChans[requestID]) + delete(h.responseChans, requestID) + delete(h.responsesCount, requestID) } else { - h.responsesCount[reqID.RequestID] = expectedResponses{ - expected: h.responsesCount[reqID.RequestID].expected, + h.responsesCount[requestID] = expectedResponses{ + expected: responses.expected, received: received, } } From 65a9162cb7f5970e06a5086fa00a4fb8cb93e036 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 21 May 2024 11:18:10 -0500 Subject: [PATCH 31/49] request id mutual exclusion --- relayer/application_relayer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index b1548ef2..ebd1bd3e 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -157,10 +157,11 @@ func (r *ApplicationRelayer) ProcessMessage(msg messages.MessageHandler) error { // Increment the request ID. Make sure we don't hold the lock while we relay the message. r.lock.Lock() r.currentRequestID++ + reqID := r.currentRequestID r.lock.Unlock() err := r.relayMessage( - r.currentRequestID, + reqID, msg, true, ) From ef461da1d7e8eac12ecd88d7e91aa1881f0d6d94 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 21 May 2024 14:16:51 -0500 Subject: [PATCH 32/49] comments and cleanup --- database/utils.go | 2 +- main/main.go | 161 +++++++++++++++++++-------------- peers/external_handler.go | 1 + relayer/application_relayer.go | 2 +- relayer/listener.go | 40 +++++++- types/types.go | 6 +- 6 files changed, 137 insertions(+), 75 deletions(-) diff --git a/database/utils.go b/database/utils.go index 8c56d083..00d1f741 100644 --- a/database/utils.go +++ b/database/utils.go @@ -45,7 +45,7 @@ func CalculateStartingBlockHeight( } else if err != nil { // Otherwise, we've encountered an unknown database error logger.Error( - "failed to get latest block from database", + "Failed to get latest block from database", zap.String("relayerID", relayerID.ID.String()), zap.Error(err), ) diff --git a/main/main.go b/main/main.go index 15402d08..7972c5f9 100644 --- a/main/main.go +++ b/main/main.go @@ -5,7 +5,6 @@ package main import ( "context" - "encoding/hex" "fmt" "log" "net/http" @@ -280,13 +279,7 @@ func runListener( manualWarpMessages []*relayerTypes.WarpMessageInfo, cfg *config.Config, ) error { - // Create the ApplicationRelayers - logger.Info( - "Creating application relayers", - zap.String("originBlockchainID", sourceBlockchain.BlockchainID), - ) - applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) - + // Dial the eth client ethClient, err := ethclient.DialWithConfig( context.Background(), sourceBlockchain.RPCEndpoint.BaseURL, @@ -302,17 +295,104 @@ func runListener( return err } + // Create the ApplicationRelayers + applicationRelayers, minHeight, err := createApplicationRelayers( + ctx, + logger, + metrics, + db, + ticker, + sourceBlockchain, + network, + messageCreator, + cfg, + ethClient, + ) + 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), + ) + + // Create the Listener + listener, err := relayer.NewListener( + logger, + metrics, + db, + ticker, + sourceBlockchain, + network, + destinationClients, + messageCreator, + relayerHealth, + cfg, + applicationRelayers, + minHeight, + ethClient, + ) + if err != nil { + return fmt.Errorf("failed to create listener instance: %w", err) + } + 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), + ) + } + + 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 createApplicationRelayers( + ctx context.Context, + logger logging.Logger, + metrics *relayer.ApplicationRelayerMetrics, + db database.RelayerDatabase, + ticker *utils.Ticker, + sourceBlockchain config.SourceBlockchain, + network *peers.AppRequestNetwork, + messageCreator message.Creator, + cfg *config.Config, + ethClient ethclient.Client, +) (map[common.Hash]*relayer.ApplicationRelayer, uint64, error) { + // Create the ApplicationRelayers + logger.Info( + "Creating application relayers", + zap.String("originBlockchainID", sourceBlockchain.BlockchainID), + ) + applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) + currentHeight, err := ethClient.BlockNumber(context.Background()) if err != nil { logger.Error( "Failed to get current block height", zap.Error(err), ) - return err + return nil, 0, err } // Each ApplicationRelayer determines its starting height based on the database state. - // The Listener back-processes from the minimum height across all of the ApplicationRelayers. + // The Listener begins processing messages starting from the minimum height across all of the ApplicationRelayers minHeight := uint64(0) for _, relayerID := range database.GetSourceBlockchainRelayerIDs(&sourceBlockchain) { height, err := database.CalculateStartingBlockHeight( @@ -328,7 +408,7 @@ func runListener( zap.String("relayerID", relayerID.ID.String()), zap.Error(err), ) - return err + return nil, 0, err } if minHeight == 0 || height < minHeight { minHeight = height @@ -351,66 +431,11 @@ func runListener( zap.String("relayerID", relayerID.ID.String()), zap.Error(err), ) - return err + return nil, 0, err } applicationRelayers[relayerID.ID] = applicationRelayer } - - // Create the Listener - logger.Info( - "Creating listener", - zap.String("originBlockchainID", sourceBlockchain.BlockchainID), - ) - listener, err := relayer.NewListener( - logger, - metrics, - db, - ticker, - sourceBlockchain, - network, - destinationClients, - messageCreator, - relayerHealth, - cfg, - applicationRelayers, - minHeight, - ethClient, - ) - if err != nil { - return fmt.Errorf("failed to create listener instance: %w", err) - } - logger.Info( - "Created listener", - zap.String("blockchainID", sourceBlockchain.BlockchainID), - ) - - // 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("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), - ) - appRelayer, handler, err := listener.GetAppRelayerMessageHandler(warpMessage) - if err != nil { - logger.Error( - "Failed to parse manual Warp message. Continuing.", - zap.Error(err), - zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), - ) - continue - } - appRelayer.ProcessMessage(handler) - } - - 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) + return applicationRelayers, minHeight, nil } func startMetricsServer(logger logging.Logger, gatherer prometheus.Gatherer, port uint16) { diff --git a/peers/external_handler.go b/peers/external_handler.go index 9ef7a077..60e14a1c 100644 --- a/peers/external_handler.go +++ b/peers/external_handler.go @@ -192,6 +192,7 @@ func (h *RelayerExternalHandler) registerAppResponse(inboundMessage message.Inbo } // Check for the expected number of responses, and clear from the map if all expected responses have been received + // TODO: we can improve performance here by independently locking the response channel and response count maps responses := h.responsesCount[requestID] received := responses.received + 1 if received == responses.expected { diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index ebd1bd3e..052634d0 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -125,6 +125,7 @@ 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 cosntructor func (r *ApplicationRelayer) ProcessHeight(height uint64, msgs []messages.MessageHandler) error { var eg errgroup.Group for _, msg := range msgs { @@ -429,7 +430,6 @@ func (r *ApplicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval if responsesExpected > 0 { // Handle the responses. For each response, we need to call response.OnFinishedHandling() exactly once. // Wrap the loop body in an anonymous function so that we do so on each loop iteration - // TODO: In order to run this concurrently, we need to route to each application relayer from the relayer responseChan for response := range responseChan { r.logger.Debug( "Processing response from node", diff --git a/relayer/listener.go b/relayer/listener.go index d7a9cd6e..2f4836b8 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -5,6 +5,7 @@ package relayer import ( "context" + "encoding/hex" "fmt" "math/big" "math/rand" @@ -363,7 +364,10 @@ func (lstnr *Listener) getApplicationRelayer( return nil } -// TODONOW: Does this function make sense? There's probably a better way to organize this logic. +// 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, @@ -419,3 +423,37 @@ func (lstnr *Listener) GetAppRelayerMessageHandler(warpMessageInfo *relayerTypes } 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("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), + ) + appRelayer, handler, err := lstnr.GetAppRelayerMessageHandler(warpMessage) + if err != nil { + logger.Error( + "Failed to parse manual Warp message.", + zap.Error(err), + zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), + ) + return err + } + err = appRelayer.ProcessMessage(handler) + if err != nil { + logger.Error( + "Failed to process manual Warp message", + zap.String("blockchainID", sourceBlockchain.BlockchainID), + zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), + ) + return err + } + } + return nil +} diff --git a/types/types.go b/types/types.go index 4a7ca793..094a1629 100644 --- a/types/types.go +++ b/types/types.go @@ -9,7 +9,6 @@ import ( "math/big" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/awm-relayer/messages" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/ethclient" "github.com/ava-labs/subnet-evm/interfaces" @@ -35,7 +34,6 @@ type WarpBlockInfo struct { type WarpMessageInfo struct { SourceAddress common.Address UnsignedMessage *avalancheWarp.UnsignedMessage - MessageManager messages.MessageManager } // Extract Warp logs from the block, if they exist @@ -79,14 +77,14 @@ func NewWarpMessageInfo(log types.Log) (*WarpMessageInfo, error) { if log.Topics[0] != WarpPrecompileLogFilter { return nil, ErrInvalidLog } - unsignedMsgBytes, err := UnpackWarpMessage(log.Data) + unsignedMsg, err := UnpackWarpMessage(log.Data) if err != nil { return nil, err } return &WarpMessageInfo{ SourceAddress: common.BytesToAddress(log.Topics[1][:]), - UnsignedMessage: unsignedMsgBytes, + UnsignedMessage: unsignedMsg, }, nil } From cbddf3a64501af17537751fa91e8ec19a64e52dd Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 21 May 2024 15:00:26 -0500 Subject: [PATCH 33/49] rename MessageManager to MessageHandlerFactory --- ...{message_manager.go => message_handler.go} | 4 +- messages/mocks/mock_message_handler.go | 142 ++++++++++++++++++ messages/mocks/mock_message_manager.go | 84 ----------- ...{message_manager.go => message_handler.go} | 30 ++-- ...anager_test.go => message_handler_test.go} | 2 +- ...{message_manager.go => message_handler.go} | 49 +++--- ...anager_test.go => message_handler_test.go} | 2 +- relayer/listener.go | 62 ++++---- 8 files changed, 219 insertions(+), 156 deletions(-) rename messages/{message_manager.go => message_handler.go} (95%) create mode 100644 messages/mocks/mock_message_handler.go delete mode 100644 messages/mocks/mock_message_manager.go rename messages/off-chain-registry/{message_manager.go => message_handler.go} (88%) rename messages/off-chain-registry/{message_manager_test.go => message_handler_test.go} (99%) rename messages/teleporter/{message_manager.go => message_handler.go} (88%) rename messages/teleporter/{message_manager_test.go => message_handler_test.go} (99%) diff --git a/messages/message_manager.go b/messages/message_handler.go similarity index 95% rename from messages/message_manager.go rename to messages/message_handler.go index 7d5a39cd..665ebd8f 100644 --- a/messages/message_manager.go +++ b/messages/message_handler.go @@ -1,7 +1,7 @@ // Copyright (C) 2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -//go:generate mockgen -source=$GOFILE -destination=./mocks/mock_message_manager.go -package=mocks +//go:generate mockgen -source=$GOFILE -destination=./mocks/mock_message_handler.go -package=mocks package messages @@ -13,7 +13,7 @@ import ( // MessageManager is specific to each message protocol. The interface handles choosing which messages to send // for each message protocol, and performs the sending to the destination chain. -type MessageManager interface { +type MessageHandlerFactory interface { // Create a message handler to relay the Warp message NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (MessageHandler, error) } diff --git a/messages/mocks/mock_message_handler.go b/messages/mocks/mock_message_handler.go new file mode 100644 index 00000000..18c9b8e1 --- /dev/null +++ b/messages/mocks/mock_message_handler.go @@ -0,0 +1,142 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: message_handler.go +// +// Generated by this command: +// +// mockgen -source=message_handler.go -destination=./mocks/mock_message_handler.go -package=mocks +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + ids "github.com/ava-labs/avalanchego/ids" + warp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + messages "github.com/ava-labs/awm-relayer/messages" + common "github.com/ethereum/go-ethereum/common" + gomock "go.uber.org/mock/gomock" +) + +// MockMessageHandlerFactory is a mock of MessageHandlerFactory interface. +type MockMessageHandlerFactory struct { + ctrl *gomock.Controller + recorder *MockMessageHandlerFactoryMockRecorder +} + +// MockMessageHandlerFactoryMockRecorder is the mock recorder for MockMessageHandlerFactory. +type MockMessageHandlerFactoryMockRecorder struct { + mock *MockMessageHandlerFactory +} + +// NewMockMessageHandlerFactory creates a new mock instance. +func NewMockMessageHandlerFactory(ctrl *gomock.Controller) *MockMessageHandlerFactory { + mock := &MockMessageHandlerFactory{ctrl: ctrl} + mock.recorder = &MockMessageHandlerFactoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMessageHandlerFactory) EXPECT() *MockMessageHandlerFactoryMockRecorder { + return m.recorder +} + +// NewMessageHandler mocks base method. +func (m *MockMessageHandlerFactory) NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (messages.MessageHandler, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewMessageHandler", unsignedMessage) + ret0, _ := ret[0].(messages.MessageHandler) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewMessageHandler indicates an expected call of NewMessageHandler. +func (mr *MockMessageHandlerFactoryMockRecorder) NewMessageHandler(unsignedMessage any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewMessageHandler", reflect.TypeOf((*MockMessageHandlerFactory)(nil).NewMessageHandler), unsignedMessage) +} + +// MockMessageHandler is a mock of MessageHandler interface. +type MockMessageHandler struct { + ctrl *gomock.Controller + recorder *MockMessageHandlerMockRecorder +} + +// MockMessageHandlerMockRecorder is the mock recorder for MockMessageHandler. +type MockMessageHandlerMockRecorder struct { + mock *MockMessageHandler +} + +// NewMockMessageHandler creates a new mock instance. +func NewMockMessageHandler(ctrl *gomock.Controller) *MockMessageHandler { + mock := &MockMessageHandler{ctrl: ctrl} + mock.recorder = &MockMessageHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMessageHandler) EXPECT() *MockMessageHandlerMockRecorder { + return m.recorder +} + +// GetMessageRoutingInfo mocks base method. +func (m *MockMessageHandler) GetMessageRoutingInfo() (ids.ID, common.Address, ids.ID, common.Address, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMessageRoutingInfo") + ret0, _ := ret[0].(ids.ID) + ret1, _ := ret[1].(common.Address) + ret2, _ := ret[2].(ids.ID) + ret3, _ := ret[3].(common.Address) + ret4, _ := ret[4].(error) + return ret0, ret1, ret2, ret3, ret4 +} + +// GetMessageRoutingInfo indicates an expected call of GetMessageRoutingInfo. +func (mr *MockMessageHandlerMockRecorder) GetMessageRoutingInfo() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessageRoutingInfo", reflect.TypeOf((*MockMessageHandler)(nil).GetMessageRoutingInfo)) +} + +// GetUnsignedMessage mocks base method. +func (m *MockMessageHandler) GetUnsignedMessage() *warp.UnsignedMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUnsignedMessage") + ret0, _ := ret[0].(*warp.UnsignedMessage) + return ret0 +} + +// GetUnsignedMessage indicates an expected call of GetUnsignedMessage. +func (mr *MockMessageHandlerMockRecorder) GetUnsignedMessage() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUnsignedMessage", reflect.TypeOf((*MockMessageHandler)(nil).GetUnsignedMessage)) +} + +// SendMessage mocks base method. +func (m *MockMessageHandler) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMessage", signedMessage, destinationBlockchainID) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMessage indicates an expected call of SendMessage. +func (mr *MockMessageHandlerMockRecorder) SendMessage(signedMessage, destinationBlockchainID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockMessageHandler)(nil).SendMessage), signedMessage, destinationBlockchainID) +} + +// ShouldSendMessage mocks base method. +func (m *MockMessageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ShouldSendMessage", destinationBlockchainID) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ShouldSendMessage indicates an expected call of ShouldSendMessage. +func (mr *MockMessageHandlerMockRecorder) ShouldSendMessage(destinationBlockchainID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShouldSendMessage", reflect.TypeOf((*MockMessageHandler)(nil).ShouldSendMessage), destinationBlockchainID) +} diff --git a/messages/mocks/mock_message_manager.go b/messages/mocks/mock_message_manager.go deleted file mode 100644 index 4e4bb4b4..00000000 --- a/messages/mocks/mock_message_manager.go +++ /dev/null @@ -1,84 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: message_manager.go -// -// Generated by this command: -// -// mockgen -source=message_manager.go -destination=./mocks/mock_message_manager.go -package=mocks -// -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - ids "github.com/ava-labs/avalanchego/ids" - warp "github.com/ava-labs/avalanchego/vms/platformvm/warp" - gomock "go.uber.org/mock/gomock" -) - -// MockMessageManager is a mock of MessageManager interface. -type MockMessageManager struct { - ctrl *gomock.Controller - recorder *MockMessageManagerMockRecorder -} - -// MockMessageManagerMockRecorder is the mock recorder for MockMessageManager. -type MockMessageManagerMockRecorder struct { - mock *MockMessageManager -} - -// NewMockMessageManager creates a new mock instance. -func NewMockMessageManager(ctrl *gomock.Controller) *MockMessageManager { - mock := &MockMessageManager{ctrl: ctrl} - mock.recorder = &MockMessageManagerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockMessageManager) EXPECT() *MockMessageManagerMockRecorder { - return m.recorder -} - -// GetDestinationBlockchainID mocks base method. -func (m *MockMessageManager) GetDestinationBlockchainID(unsignedMessage *warp.UnsignedMessage) (ids.ID, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDestinationBlockchainID", unsignedMessage) - ret0, _ := ret[0].(ids.ID) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetDestinationBlockchainID indicates an expected call of GetDestinationBlockchainID. -func (mr *MockMessageManagerMockRecorder) GetDestinationBlockchainID(unsignedMessage any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDestinationBlockchainID", reflect.TypeOf((*MockMessageManager)(nil).GetDestinationBlockchainID), unsignedMessage) -} - -// SendMessage mocks base method. -func (m *MockMessageManager) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SendMessage", signedMessage, destinationBlockchainID) - ret0, _ := ret[0].(error) - return ret0 -} - -// SendMessage indicates an expected call of SendMessage. -func (mr *MockMessageManagerMockRecorder) SendMessage(signedMessage, destinationBlockchainID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockMessageManager)(nil).SendMessage), signedMessage, destinationBlockchainID) -} - -// ShouldSendMessage mocks base method. -func (m *MockMessageManager) ShouldSendMessage(unsignedMessage *warp.UnsignedMessage, destinationBlockchainID ids.ID) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ShouldSendMessage", unsignedMessage, destinationBlockchainID) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ShouldSendMessage indicates an expected call of ShouldSendMessage. -func (mr *MockMessageManagerMockRecorder) ShouldSendMessage(unsignedMessage, destinationBlockchainID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShouldSendMessage", reflect.TypeOf((*MockMessageManager)(nil).ShouldSendMessage), unsignedMessage, destinationBlockchainID) -} diff --git a/messages/off-chain-registry/message_manager.go b/messages/off-chain-registry/message_handler.go similarity index 88% rename from messages/off-chain-registry/message_manager.go rename to messages/off-chain-registry/message_handler.go index 59075ba4..34e035c3 100644 --- a/messages/off-chain-registry/message_manager.go +++ b/messages/off-chain-registry/message_handler.go @@ -29,7 +29,7 @@ const ( revertVersionNotFoundString = "TeleporterRegistry: version not found" ) -type messageManager struct { +type factory struct { logger logging.Logger destinationClients map[ids.ID]vms.DestinationClient registryAddress common.Address @@ -38,14 +38,14 @@ type messageManager struct { type messageHandler struct { logger logging.Logger unsignedMessage *warp.UnsignedMessage - messageManager *messageManager + factory *factory } -func NewMessageManager( +func NewMessageHandlerFactory( logger logging.Logger, messageProtocolConfig config.MessageProtocolConfig, destinationClients map[ids.ID]vms.DestinationClient, -) (*messageManager, error) { +) (*factory, error) { // Marshal the map and unmarshal into the off-chain registry config data, err := json.Marshal(messageProtocolConfig.Settings) if err != nil { @@ -65,18 +65,18 @@ func NewMessageManager( ) return nil, err } - return &messageManager{ + return &factory{ logger: logger, destinationClients: destinationClients, registryAddress: common.HexToAddress(messageConfig.TeleporterRegistryAddress), }, nil } -func (m *messageManager) NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (messages.MessageHandler, error) { +func (f *factory) NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (messages.MessageHandler, error) { return &messageHandler{ - logger: m.logger, + logger: f.logger, unsignedMessage: unsignedMessage, - messageManager: m, + factory: f, }, nil } @@ -103,17 +103,17 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool ) return false, err } - if destination != m.messageManager.registryAddress { + if destination != m.factory.registryAddress { m.logger.Info( "Message is not intended for the configured registry", zap.String("destination", destination.String()), - zap.String("configuredRegistry", m.messageManager.registryAddress.String()), + zap.String("configuredRegistry", m.factory.registryAddress.String()), ) return false, nil } // Get the correct destination client from the global map - destinationClient, ok := m.messageManager.destinationClients[destinationBlockchainID] + destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] if !ok { return false, fmt.Errorf("relayer not configured to deliver to destination. destinationBlockchainID=%s", destinationBlockchainID.String()) } @@ -123,7 +123,7 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool } // Check if the version is already registered in the TeleporterRegistry contract. - registry, err := teleporterregistry.NewTeleporterRegistryCaller(m.messageManager.registryAddress, client) + registry, err := teleporterregistry.NewTeleporterRegistryCaller(m.factory.registryAddress, client) if err != nil { m.logger.Error( "Failed to create TeleporterRegistry caller", @@ -153,7 +153,7 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { // Get the correct destination client from the global map - destinationClient, ok := m.messageManager.destinationClients[destinationBlockchainID] + destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] if !ok { return fmt.Errorf("relayer not configured to deliver to destination. DestinationBlockchainID=%s", destinationBlockchainID) } @@ -170,7 +170,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlo return err } - err = destinationClient.SendTx(signedMessage, m.messageManager.registryAddress.Hex(), addProtocolVersionGasLimit, callData) + err = destinationClient.SendTx(signedMessage, m.factory.registryAddress.Hex(), addProtocolVersionGasLimit, callData) if err != nil { m.logger.Error( "Failed to send tx.", @@ -206,6 +206,6 @@ func (m *messageHandler) GetMessageRoutingInfo() ( return m.unsignedMessage.SourceChainID, common.BytesToAddress(addressedPayload.SourceAddress), m.unsignedMessage.SourceChainID, - m.messageManager.registryAddress, + m.factory.registryAddress, nil } diff --git a/messages/off-chain-registry/message_manager_test.go b/messages/off-chain-registry/message_handler_test.go similarity index 99% rename from messages/off-chain-registry/message_manager_test.go rename to messages/off-chain-registry/message_handler_test.go index 3561734f..fccfa784 100644 --- a/messages/off-chain-registry/message_manager_test.go +++ b/messages/off-chain-registry/message_handler_test.go @@ -133,7 +133,7 @@ func TestShouldSendMessage(t *testing.T) { test.destinationBlockchainID: mockClient, } - messageManager, err := NewMessageManager( + messageManager, err := NewMessageHandlerFactory( logger, messageProtocolConfig, destinationClients, diff --git a/messages/teleporter/message_manager.go b/messages/teleporter/message_handler.go similarity index 88% rename from messages/teleporter/message_manager.go rename to messages/teleporter/message_handler.go index 30fa2879..0b6243bb 100644 --- a/messages/teleporter/message_manager.go +++ b/messages/teleporter/message_handler.go @@ -23,7 +23,7 @@ import ( "go.uber.org/zap" ) -type messageManager struct { +type factory struct { messageConfig Config protocolAddress common.Address destinationClients map[ids.ID]vms.DestinationClient @@ -34,15 +34,15 @@ type messageHandler struct { logger logging.Logger teleporterMessage *teleportermessenger.TeleporterMessage unsignedMessage *warp.UnsignedMessage - messageManager *messageManager + factory *factory } -func NewMessageManager( +func NewMessageHandlerFactory( logger logging.Logger, messageProtocolAddress common.Address, messageProtocolConfig config.MessageProtocolConfig, destinationClients map[ids.ID]vms.DestinationClient, -) (*messageManager, error) { +) (*factory, error) { // Marshal the map and unmarshal into the Teleporter config data, err := json.Marshal(messageProtocolConfig.Settings) if err != nil { @@ -63,7 +63,7 @@ func NewMessageManager( return nil, err } - return &messageManager{ + return &factory{ messageConfig: messageConfig, protocolAddress: messageProtocolAddress, destinationClients: destinationClients, @@ -71,20 +71,20 @@ func NewMessageManager( }, nil } -func (m *messageManager) NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (messages.MessageHandler, error) { - teleporterMessage, err := m.parseTeleporterMessage(unsignedMessage) +func (f *factory) NewMessageHandler(unsignedMessage *warp.UnsignedMessage) (messages.MessageHandler, error) { + teleporterMessage, err := f.parseTeleporterMessage(unsignedMessage) if err != nil { - m.logger.Error( + f.logger.Error( "Failed to parse teleporter message.", zap.String("warpMessageID", unsignedMessage.ID().String()), ) return nil, err } return &messageHandler{ - logger: m.logger, + logger: f.logger, teleporterMessage: teleporterMessage, unsignedMessage: unsignedMessage, - messageManager: m, + factory: f, }, nil } @@ -123,14 +123,14 @@ func (m *messageHandler) GetMessageRoutingInfo() ( // ShouldSendMessage returns true if the message should be sent to the destination chain func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool, error) { // Get the correct destination client from the global map - destinationClient, ok := m.messageManager.destinationClients[destinationBlockchainID] + destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] if !ok { // This shouldn't occur, since we already check this in Listener.RouteMessage. Return an error in this case. return false, fmt.Errorf("relayer not configured to deliver to destination. destinationBlockchainID=%s", destinationBlockchainID.String()) } teleporterMessageID, err := teleporterUtils.CalculateMessageID( - m.messageManager.protocolAddress, + m.factory.protocolAddress, m.unsignedMessage.SourceChainID, destinationBlockchainID, m.teleporterMessage.MessageNonce, @@ -151,7 +151,7 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool } // Check if the message has already been delivered to the destination chain - teleporterMessenger := m.messageManager.getTeleporterMessenger(destinationClient) + teleporterMessenger := m.factory.getTeleporterMessenger(destinationBlockchainID) delivered, err := teleporterMessenger.MessageReceived(&bind.CallOpts{}, teleporterMessageID) if err != nil { m.logger.Error( @@ -179,13 +179,13 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool // and dispatches transaction construction and broadcast to the destination client func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { // Get the correct destination client from the global map - destinationClient, ok := m.messageManager.destinationClients[destinationBlockchainID] + destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] if !ok { return fmt.Errorf("relayer not configured to deliver to destination. DestinationBlockchainID=%s", destinationBlockchainID) } teleporterMessageID, err := teleporterUtils.CalculateMessageID( - m.messageManager.protocolAddress, + m.factory.protocolAddress, signedMessage.SourceChainID, destinationBlockchainID, m.teleporterMessage.MessageNonce, @@ -221,7 +221,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlo return 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.messageManager.messageConfig.RewardAddress)) + callData, err := teleportermessenger.PackReceiveCrossChainMessage(0, common.HexToAddress(m.factory.messageConfig.RewardAddress)) if err != nil { m.logger.Error( "Failed packing receiveCrossChainMessage call data", @@ -232,7 +232,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlo return err } - err = destinationClient.SendTx(signedMessage, m.messageManager.protocolAddress.Hex(), gasLimit, callData) + err = destinationClient.SendTx(signedMessage, m.factory.protocolAddress.Hex(), gasLimit, callData) if err != nil { m.logger.Error( "Failed to send tx.", @@ -254,10 +254,10 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlo // parseTeleporterMessage returns the Warp message's corresponding Teleporter message from the cache if it exists. // Otherwise parses the Warp message payload. -func (m *messageManager) 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 { - m.logger.Error( + f.logger.Error( "Failed parsing addressed payload", zap.Error(err), ) @@ -265,7 +265,7 @@ func (m *messageManager) parseTeleporterMessage(unsignedMessage *warp.UnsignedMe } teleporterMessage, err := teleportermessenger.UnpackTeleporterMessage(addressedPayload.Payload) if err != nil { - m.logger.Error( + f.logger.Error( "Failed unpacking teleporter message.", zap.String("warpMessageID", unsignedMessage.ID().String()), ) @@ -278,14 +278,19 @@ func (m *messageManager) parseTeleporterMessage(unsignedMessage *warp.UnsignedMe // 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 (m *messageManager) getTeleporterMessenger(destinationClient vms.DestinationClient) *teleportermessenger.TeleporterMessenger { +func (f *factory) getTeleporterMessenger(destinationBlockchainID ids.ID) *teleportermessenger.TeleporterMessenger { + + destinationClient, ok := f.destinationClients[destinationBlockchainID] + if !ok { + return nil + } client, ok := destinationClient.Client().(ethclient.Client) if !ok { panic(fmt.Sprintf("Destination client for chain %s is not an Ethereum client", destinationClient.DestinationBlockchainID().String())) } // Get the teleporter messenger contract - teleporterMessenger, err := teleportermessenger.NewTeleporterMessenger(m.protocolAddress, client) + teleporterMessenger, err := teleportermessenger.NewTeleporterMessenger(f.protocolAddress, client) if err != nil { panic("Failed to get teleporter messenger contract") } diff --git a/messages/teleporter/message_manager_test.go b/messages/teleporter/message_handler_test.go similarity index 99% rename from messages/teleporter/message_manager_test.go rename to messages/teleporter/message_handler_test.go index d226f1b7..31a6238a 100644 --- a/messages/teleporter/message_manager_test.go +++ b/messages/teleporter/message_handler_test.go @@ -170,7 +170,7 @@ func TestShouldSendMessage(t *testing.T) { test.destinationBlockchainID: mockClient, } - messageManager, err := NewMessageManager( + messageManager, err := NewMessageHandlerFactory( logger, messageProtocolAddress, messageProtocolConfig, diff --git a/relayer/listener.go b/relayer/listener.go index 2f4836b8..0a273524 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -38,18 +38,18 @@ 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 - messageManagers map[common.Address]messages.MessageManager - 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 + 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 } func NewListener( @@ -92,24 +92,24 @@ func NewListener( sub := vms.NewSubscriber(logger, config.ParseVM(sourceBlockchain.VM), blockchainID, ethWSClient) // Create message managers for each supported message protocol - messageManagers := make(map[common.Address]messages.MessageManager) + messageHandlerFactories := make(map[common.Address]messages.MessageHandlerFactory) for addressStr, cfg := range sourceBlockchain.MessageContracts { address := common.HexToAddress(addressStr) format := cfg.MessageFormat var ( - m messages.MessageManager + m messages.MessageHandlerFactory err error ) switch config.ParseMessageProtocol(format) { case config.TELEPORTER: - m, err = teleporter.NewMessageManager( + m, err = teleporter.NewMessageHandlerFactory( logger, address, cfg, destinationClients, ) case config.OFF_CHAIN_REGISTRY: - m, err = offchainregistry.NewMessageManager( + m, err = offchainregistry.NewMessageHandlerFactory( logger, cfg, destinationClients, @@ -124,7 +124,7 @@ func NewListener( ) return nil, err } - messageManagers[address] = m + messageHandlerFactories[address] = m } // Marks when the listener has finished the catch-up process on startup. @@ -143,18 +143,18 @@ 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), - messageManagers: messageManagers, - logger: logger, - sourceBlockchain: sourceBlockchain, - catchUpResultChan: catchUpResultChan, - healthStatus: relayerHealth, - globalConfig: cfg, - applicationRelayers: applicationRelayers, - ethClient: ethClient, + 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: cfg, + applicationRelayers: applicationRelayers, + ethClient: ethClient, } // Open the subscription. We must do this before processing any missed messages, otherwise we may miss an incoming message @@ -374,7 +374,7 @@ func (lstnr *Listener) GetAppRelayerMessageHandler(warpMessageInfo *relayerTypes error, ) { // Check that the warp message is from a supported message protocol contract address. - messageManager, supportedMessageProtocol := lstnr.messageManagers[warpMessageInfo.SourceAddress] + 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. @@ -384,7 +384,7 @@ func (lstnr *Listener) GetAppRelayerMessageHandler(warpMessageInfo *relayerTypes ) return nil, nil, nil } - messageHandler, err := messageManager.NewMessageHandler(warpMessageInfo.UnsignedMessage) + messageHandler, err := messageHandlerFactory.NewMessageHandler(warpMessageInfo.UnsignedMessage) if err != nil { lstnr.logger.Error( "Failed to create message handler", From 8653be179e880734093727e81372027cbd2d9803 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 21 May 2024 15:21:36 -0500 Subject: [PATCH 34/49] lint --- messages/teleporter/message_handler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/messages/teleporter/message_handler.go b/messages/teleporter/message_handler.go index 0b6243bb..442a0982 100644 --- a/messages/teleporter/message_handler.go +++ b/messages/teleporter/message_handler.go @@ -279,7 +279,6 @@ func (f *factory) parseTeleporterMessage(unsignedMessage *warp.UnsignedMessage) // 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(destinationBlockchainID ids.ID) *teleportermessenger.TeleporterMessenger { - destinationClient, ok := f.destinationClients[destinationBlockchainID] if !ok { return nil From 610105b450d21f9e14288db920628a4b96eac2c5 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 21 May 2024 15:21:45 -0500 Subject: [PATCH 35/49] revert to go1.21 --- go.mod | 2 +- relayer/application_relayer.go | 1 + scripts/versions.sh | 2 +- tests/batch_relay.go | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 43dfe0f5..a2dd3e34 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ava-labs/awm-relayer -go 1.22.3 +go 1.21.10 require ( github.com/ava-labs/avalanche-network-runner v1.7.6 diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 052634d0..1b03f14e 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -129,6 +129,7 @@ func NewApplicationRelayer( func (r *ApplicationRelayer) ProcessHeight(height uint64, msgs []messages.MessageHandler) error { var eg errgroup.Group for _, msg := range msgs { + msg := msg eg.Go(func() error { return r.ProcessMessage(msg) }) diff --git a/scripts/versions.sh b/scripts/versions.sh index 292e87f3..3cd73919 100755 --- a/scripts/versions.sh +++ b/scripts/versions.sh @@ -22,4 +22,4 @@ GINKGO_VERSION=${GINKGO_VERSION:-$(getDepVersion github.com/onsi/ginkgo/v2)} SUBNET_EVM_VERSION=${SUBNET_EVM_VERSION:-$(getDepVersion github.com/ava-labs/subnet-evm)} # Set golangci-lint version -GOLANGCI_LINT_VERSION=${GOLANGCI_LINT_VERSION:-'v1.58.1'} +GOLANGCI_LINT_VERSION=${GOLANGCI_LINT_VERSION:-'v1.55'} diff --git a/tests/batch_relay.go b/tests/batch_relay.go index 0f81501b..808eb436 100644 --- a/tests/batch_relay.go +++ b/tests/batch_relay.go @@ -85,7 +85,7 @@ func BatchRelay(network interfaces.LocalNetwork) { numMessages := 50 sentMessages := set.NewSet[string](numMessages) - for i := range numMessages { + for i := 0; i < numMessages; i++ { sentMessages.Add(strconv.Itoa(i)) } From e939ecaf1eaae50a2f9e989f4e40ac351951515f Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Tue, 21 May 2024 16:45:56 -0500 Subject: [PATCH 36/49] add handleResponse helper --- relayer/application_relayer.go | 180 ++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 80 deletions(-) diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 1b03f14e..ed337dbc 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -436,89 +436,21 @@ func (r *ApplicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval "Processing response from node", zap.String("nodeID", response.NodeID().String()), ) - // This anonymous function attempts to create a signed warp message from the accumulated responses - // Returns an error only if a non-recoverable error occurs, otherwise returns (nil, nil) to continue processing responses - // When a non-nil signedMsg is returned, createSignedMessage itself returns - signedMsg, err := func() (*avalancheWarp.Message, error) { - defer response.OnFinishedHandling() - - // Check if this is an expected response. - m := response.Message() - rcvReqID, ok := message.GetRequestID(m) - if !ok { - // This should never occur, since inbound message validity is already checked by the inbound handler - r.logger.Error("Could not get requestID from message") - return nil, nil - } - nodeID := response.NodeID() - if !sentTo.Contains(nodeID) || rcvReqID != requestID { - r.logger.Debug("Skipping irrelevant app response") - return nil, nil - } - - // Count the relevant app message - responseCount++ - - // If we receive an AppRequestFailed, then the request timed out. - // We still want to increment responseCount, since we are no longer expecting a response from that node. - if response.Op() == message.AppErrorOp { - r.logger.Debug("Request timed out") - return nil, nil - } - - validator, vdrIndex := connectedValidators.GetValidator(nodeID) - signature, valid := r.isValidSignatureResponse(unsignedMessage, response, validator.PublicKey) - if valid { - r.logger.Debug( - "Got valid signature response", - zap.String("nodeID", nodeID.String()), - ) - signatureMap[vdrIndex] = signature - accumulatedSignatureWeight.Add(accumulatedSignatureWeight, new(big.Int).SetUint64(validator.Weight)) - } else { - r.logger.Debug( - "Got invalid signature response", - zap.String("nodeID", nodeID.String()), - ) - return nil, nil - } - - // As soon as the signatures exceed the stake weight threshold we try to aggregate and send the transaction. - if utils.CheckStakeWeightExceedsThreshold( - accumulatedSignatureWeight, - connectedValidators.TotalValidatorWeight, - r.warpQuorum.QuorumNumerator, - r.warpQuorum.QuorumDenominator, - ) { - aggSig, vdrBitSet, err := r.aggregateSignatures(signatureMap) - if err != nil { - r.logger.Error( - "Failed to aggregate signature.", - zap.String("destinationBlockchainID", r.relayerID.DestinationBlockchainID.String()), - zap.Error(err), - ) - return nil, err - } - - 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", - zap.Error(err), - ) - return nil, err - } - return signedMsg, nil - } - // Not enough signatures, continue processing messages - return nil, nil - }() + signedMsg, relevant, err := r.handleResponse( + response, + sentTo, + requestID, + connectedValidators, + unsignedMessage, + signatureMap, + accumulatedSignatureWeight, + ) if err != nil { return nil, err } + if relevant { + responseCount++ + } // If we have sufficient signatures, return here. if signedMsg != nil { r.logger.Info( @@ -548,6 +480,94 @@ func (r *ApplicationRelayer) createSignedMessageAppRequest(unsignedMessage *aval return nil, errNotEnoughSignatures } +// 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. +func (r *ApplicationRelayer) handleResponse( + response message.InboundMessage, + sentTo set.Set[ids.NodeID], + requestID uint32, + connectedValidators *peers.ConnectedCanonicalValidators, + unsignedMessage *avalancheWarp.UnsignedMessage, + signatureMap map[int]blsSignatureBuf, + accumulatedSignatureWeight *big.Int, +) (*avalancheWarp.Message, bool, error) { + // Regardless of the response's relevance, call it's finished handler once this function returns + defer response.OnFinishedHandling() + + // Check if this is an expected response. + m := response.Message() + rcvReqID, ok := message.GetRequestID(m) + if !ok { + // This should never occur, since inbound message validity is already checked by the inbound handler + r.logger.Error("Could not get requestID from message") + return nil, false, nil + } + nodeID := response.NodeID() + if !sentTo.Contains(nodeID) || rcvReqID != requestID { + r.logger.Debug("Skipping irrelevant app response") + return nil, false, nil + } + + // If we receive an AppRequestFailed, then the request timed out. + // This is still a relevant response, since we are no longer expecting a response from that node. + if response.Op() == message.AppErrorOp { + r.logger.Debug("Request timed out") + return nil, true, nil + } + + validator, vdrIndex := connectedValidators.GetValidator(nodeID) + signature, valid := r.isValidSignatureResponse(unsignedMessage, response, validator.PublicKey) + if valid { + r.logger.Debug( + "Got valid signature response", + zap.String("nodeID", nodeID.String()), + ) + signatureMap[vdrIndex] = signature + accumulatedSignatureWeight.Add(accumulatedSignatureWeight, new(big.Int).SetUint64(validator.Weight)) + } else { + r.logger.Debug( + "Got invalid signature response", + zap.String("nodeID", nodeID.String()), + ) + return nil, true, nil + } + + // As soon as the signatures exceed the stake weight threshold we try to aggregate and send the transaction. + if utils.CheckStakeWeightExceedsThreshold( + accumulatedSignatureWeight, + connectedValidators.TotalValidatorWeight, + r.warpQuorum.QuorumNumerator, + r.warpQuorum.QuorumDenominator, + ) { + aggSig, vdrBitSet, err := r.aggregateSignatures(signatureMap) + if err != nil { + r.logger.Error( + "Failed to aggregate signature.", + zap.String("destinationBlockchainID", r.relayerID.DestinationBlockchainID.String()), + zap.Error(err), + ) + return nil, true, err + } + + 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", + zap.Error(err), + ) + return nil, true, err + } + return signedMsg, true, nil + } + // Not enough signatures, continue processing messages + 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. func (r *ApplicationRelayer) isValidSignatureResponse( From 0611a3bf817d31acd3b897752daf518cabe7e067 Mon Sep 17 00:00:00 2001 From: cam-schultz <78878559+cam-schultz@users.noreply.github.com> Date: Wed, 22 May 2024 14:38:44 -0500 Subject: [PATCH 37/49] Update relayer/application_relayer.go Co-authored-by: Geoff Stuart Signed-off-by: cam-schultz <78878559+cam-schultz@users.noreply.github.com> --- 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 ed337dbc..7711432e 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -125,7 +125,7 @@ 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 cosntructor +// 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, msgs []messages.MessageHandler) error { var eg errgroup.Group for _, msg := range msgs { From b920a0f4d608e141e1d08afa0b424b27ea577112 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Wed, 22 May 2024 14:40:13 -0500 Subject: [PATCH 38/49] rename shadowed var --- relayer/application_relayer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index ed337dbc..537f6906 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -126,12 +126,12 @@ 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 cosntructor -func (r *ApplicationRelayer) ProcessHeight(height uint64, msgs []messages.MessageHandler) error { +func (r *ApplicationRelayer) ProcessHeight(height uint64, messages []messages.MessageHandler) error { var eg errgroup.Group - for _, msg := range msgs { - msg := msg + for _, message := range messages { + m := message eg.Go(func() error { - return r.ProcessMessage(msg) + return r.ProcessMessage(m) }) } if err := eg.Wait(); err != nil { From 757f98872c028fcbad9045e205d95f31afc9e698 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Wed, 22 May 2024 15:13:16 -0500 Subject: [PATCH 39/49] cleanup --- main/main.go | 7 ++----- messages/teleporter/message_handler.go | 4 ++-- relayer/application_relayer.go | 9 ++++----- relayer/checkpoint/checkpoint.go | 3 ++- relayer/listener.go | 15 +++------------ 5 files changed, 13 insertions(+), 25 deletions(-) diff --git a/main/main.go b/main/main.go index 7972c5f9..7e4698c1 100644 --- a/main/main.go +++ b/main/main.go @@ -5,6 +5,7 @@ package main import ( "context" + "encoding/hex" "fmt" "log" "net/http" @@ -210,6 +211,7 @@ func main() { if err != nil { logger.Error( "Failed to unpack manual Warp message", + zap.String("warpMessageBytes", hex.EncodeToString(msg.GetUnsignedMessageBytes())), zap.Error(err), ) panic(err) @@ -324,13 +326,8 @@ func runListener( // Create the Listener listener, err := relayer.NewListener( logger, - metrics, - db, - ticker, sourceBlockchain, - network, destinationClients, - messageCreator, relayerHealth, cfg, applicationRelayers, diff --git a/messages/teleporter/message_handler.go b/messages/teleporter/message_handler.go index 442a0982..c5585650 100644 --- a/messages/teleporter/message_handler.go +++ b/messages/teleporter/message_handler.go @@ -125,7 +125,7 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool // Get the correct destination client from the global map destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] if !ok { - // This shouldn't occur, since we already check this in Listener.RouteMessage. Return an error in this case. + // This shouldn't occur, since the destination client map and message handler factories are built from the same configuration. return false, fmt.Errorf("relayer not configured to deliver to destination. destinationBlockchainID=%s", destinationBlockchainID.String()) } @@ -291,7 +291,7 @@ func (f *factory) getTeleporterMessenger(destinationBlockchainID ids.ID) *telepo // Get the teleporter messenger contract teleporterMessenger, err := teleportermessenger.NewTeleporterMessenger(f.protocolAddress, client) if err != nil { - panic("Failed to get teleporter messenger contract") + panic(fmt.Sprintf("Failed to get teleporter messenger contract: %w", err)) } return teleporterMessenger } diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 537f6906..032402bc 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -53,10 +53,9 @@ var ( errNotEnoughConnectedStake = errors.New("failed to connect to a threshold of stake") ) -// ApplicationRelayers are created for each warp message to be relayed. -// They collect signatures from validators, aggregate them, -// and send the signed warp message to the destination chain. -// Each ApplicationRelayer runs in its own goroutine. +// ApplicationRelayers define a Warp message route from a specific source address on a specific source blockchain +// to a specific destination address on a specific destination blockchain. This routing information is +// encapsulated in [relayerID], which also represents the database key for an ApplicationRelayer. type ApplicationRelayer struct { logger logging.Logger metrics *ApplicationRelayerMetrics @@ -149,7 +148,7 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, messages []messages.Me zap.Uint64("height", height), zap.String("sourceBlockchainID", r.relayerID.SourceBlockchainID.String()), zap.String("relayerID", r.relayerID.ID.String()), - zap.Int("numMessages", len(msgs)), + zap.Int("numMessages", len(messages)), ) return nil } diff --git a/relayer/checkpoint/checkpoint.go b/relayer/checkpoint/checkpoint.go index a5c7f2bb..6d0e0e08 100644 --- a/relayer/checkpoint/checkpoint.go +++ b/relayer/checkpoint/checkpoint.go @@ -102,8 +102,9 @@ func (cm *CheckpointManager) listenForWriteSignal() { // Heights are committed in sequence, so if height is not exactly one // greater than the current committedHeight, it is instead cached in memory // to potentially be committed later. -// Requires that cm.lock be held func (cm *CheckpointManager) StageCommittedHeight(height uint64) { + cm.lock.Lock() + defer cm.lock.Unlock() if height <= cm.committedHeight { cm.logger.Debug( "Attempting to commit height less than or equal to the committed height. Skipping.", diff --git a/relayer/listener.go b/relayer/listener.go index 0a273524..23363bca 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -5,14 +5,12 @@ package relayer import ( "context" - "encoding/hex" "fmt" "math/big" "math/rand" "sync" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/database" @@ -20,9 +18,7 @@ import ( "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" relayerTypes "github.com/ava-labs/awm-relayer/types" - "github.com/ava-labs/awm-relayer/utils" vms "github.com/ava-labs/awm-relayer/vms" "github.com/ethereum/go-ethereum/common" "go.uber.org/atomic" @@ -54,13 +50,8 @@ type Listener struct { func NewListener( logger logging.Logger, - metrics *ApplicationRelayerMetrics, - db database.RelayerDatabase, - ticker *utils.Ticker, sourceBlockchain config.SourceBlockchain, - network *peers.AppRequestNetwork, destinationClients map[ids.ID]vms.DestinationClient, - messageCreator message.Creator, relayerHealth *atomic.Bool, cfg *config.Config, applicationRelayers map[common.Hash]*ApplicationRelayer, @@ -434,14 +425,14 @@ func (lstnr *Listener) ProcessManualWarpMessages( logger.Info( "Relaying manual Warp message", zap.String("blockchainID", sourceBlockchain.BlockchainID), - zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), + 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("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), + zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), ) return err } @@ -450,7 +441,7 @@ func (lstnr *Listener) ProcessManualWarpMessages( logger.Error( "Failed to process manual Warp message", zap.String("blockchainID", sourceBlockchain.BlockchainID), - zap.String("warpMessageBytes", hex.EncodeToString(warpMessage.UnsignedMessage.Bytes())), + zap.String("warpMessageID", warpMessage.UnsignedMessage.ID().String()), ) return err } From 359dde436554af39c9b78156e93ceb6858ffb52e Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Wed, 22 May 2024 15:13:40 -0500 Subject: [PATCH 40/49] process blocks async --- relayer/listener.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/relayer/listener.go b/relayer/listener.go index 23363bca..a9df5dd0 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -246,7 +246,10 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { // 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] - appRelayer.ProcessHeight(block.BlockNumber, handlers) + + // 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) } case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) From 00a1d4e606f68fb1718776b99780a277cd31fc65 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Wed, 22 May 2024 15:25:34 -0500 Subject: [PATCH 41/49] app relayer holds a dst client --- main/main.go | 8 ++-- messages/message_handler.go | 5 ++- .../off-chain-registry/message_handler.go | 33 +++++--------- messages/teleporter/message_handler.go | 44 ++++++------------- relayer/application_relayer.go | 26 ++++++----- relayer/listener.go | 3 -- 6 files changed, 46 insertions(+), 73 deletions(-) diff --git a/main/main.go b/main/main.go index 7e4698c1..ff2a9093 100644 --- a/main/main.go +++ b/main/main.go @@ -309,6 +309,7 @@ func runListener( messageCreator, cfg, ethClient, + destinationClients, ) if err != nil { logger.Error( @@ -327,7 +328,6 @@ func runListener( listener, err := relayer.NewListener( logger, sourceBlockchain, - destinationClients, relayerHealth, cfg, applicationRelayers, @@ -370,7 +370,8 @@ func createApplicationRelayers( network *peers.AppRequestNetwork, messageCreator message.Creator, cfg *config.Config, - ethClient ethclient.Client, + srcEthClient ethclient.Client, + destinationClients map[ids.ID]vms.DestinationClient, ) (map[common.Hash]*relayer.ApplicationRelayer, uint64, error) { // Create the ApplicationRelayers logger.Info( @@ -379,7 +380,7 @@ func createApplicationRelayers( ) applicationRelayers := make(map[common.Hash]*relayer.ApplicationRelayer) - currentHeight, err := ethClient.BlockNumber(context.Background()) + currentHeight, err := srcEthClient.BlockNumber(context.Background()) if err != nil { logger.Error( "Failed to get current block height", @@ -418,6 +419,7 @@ func createApplicationRelayers( relayerID, db, ticker, + destinationClients[relayerID.DestinationBlockchainID], sourceBlockchain, height, cfg, diff --git a/messages/message_handler.go b/messages/message_handler.go index 665ebd8f..1ebfc04c 100644 --- a/messages/message_handler.go +++ b/messages/message_handler.go @@ -8,6 +8,7 @@ package messages import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/awm-relayer/vms" "github.com/ethereum/go-ethereum/common" ) @@ -22,11 +23,11 @@ type MessageHandlerFactory interface { type MessageHandler interface { // ShouldSendMessage returns true if the message should be sent to the destination chain // If an error is returned, the boolean should be ignored by the caller. - ShouldSendMessage(destinationBlockchainID ids.ID) (bool, error) + ShouldSendMessage(destinationClient vms.DestinationClient) (bool, error) // 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, destinationBlockchainID ids.ID) error + SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) 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 34e035c3..f6b0d548 100644 --- a/messages/off-chain-registry/message_handler.go +++ b/messages/off-chain-registry/message_handler.go @@ -30,9 +30,8 @@ const ( ) type factory struct { - logger logging.Logger - destinationClients map[ids.ID]vms.DestinationClient - registryAddress common.Address + logger logging.Logger + registryAddress common.Address } type messageHandler struct { @@ -44,8 +43,7 @@ type messageHandler struct { func NewMessageHandlerFactory( logger logging.Logger, messageProtocolConfig config.MessageProtocolConfig, - destinationClients map[ids.ID]vms.DestinationClient, -) (*factory, error) { +) (messages.MessageHandlerFactory, error) { // Marshal the map and unmarshal into the off-chain registry config data, err := json.Marshal(messageProtocolConfig.Settings) if err != nil { @@ -66,9 +64,8 @@ func NewMessageHandlerFactory( return nil, err } return &factory{ - logger: logger, - destinationClients: destinationClients, - registryAddress: common.HexToAddress(messageConfig.TeleporterRegistryAddress), + logger: logger, + registryAddress: common.HexToAddress(messageConfig.TeleporterRegistryAddress), }, nil } @@ -86,7 +83,7 @@ func (m *messageHandler) GetUnsignedMessage() *warp.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. -func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool, error) { +func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClient) (bool, error) { addressedPayload, err := warpPayload.ParseAddressedCall(m.unsignedMessage.Payload) if err != nil { m.logger.Error( @@ -113,10 +110,6 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool } // Get the correct destination client from the global map - destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] - if !ok { - return false, fmt.Errorf("relayer not configured to deliver to destination. destinationBlockchainID=%s", destinationBlockchainID.String()) - } client, ok := destinationClient.Client().(ethclient.Client) if !ok { panic(fmt.Sprintf("Destination client for chain %s is not an Ethereum client", destinationClient.DestinationBlockchainID().String())) @@ -151,20 +144,14 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool return false, nil } -func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlockchainID ids.ID) error { - // Get the correct destination client from the global map - destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] - if !ok { - return fmt.Errorf("relayer not configured to deliver to destination. DestinationBlockchainID=%s", destinationBlockchainID) - } - +func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) 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", destinationBlockchainID.String()), + zap.String("destinationBlockchainID", destinationClient.DestinationBlockchainID().String()), zap.String("warpMessageID", signedMessage.ID().String()), ) return err @@ -174,7 +161,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlo if err != nil { m.logger.Error( "Failed to send tx.", - zap.String("destinationBlockchainID", destinationBlockchainID.String()), + zap.String("destinationBlockchainID", destinationClient.DestinationBlockchainID().String()), zap.String("warpMessageID", signedMessage.ID().String()), zap.Error(err), ) @@ -182,7 +169,7 @@ func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationBlo } m.logger.Info( "Sent message to destination chain", - zap.String("destinationBlockchainID", destinationBlockchainID.String()), + zap.String("destinationBlockchainID", destinationClient.DestinationBlockchainID().String()), zap.String("warpMessageID", signedMessage.ID().String()), ) return nil diff --git a/messages/teleporter/message_handler.go b/messages/teleporter/message_handler.go index c5585650..25acd199 100644 --- a/messages/teleporter/message_handler.go +++ b/messages/teleporter/message_handler.go @@ -24,10 +24,9 @@ import ( ) type factory struct { - messageConfig Config - protocolAddress common.Address - destinationClients map[ids.ID]vms.DestinationClient - logger logging.Logger + messageConfig Config + protocolAddress common.Address + logger logging.Logger } type messageHandler struct { @@ -41,8 +40,7 @@ func NewMessageHandlerFactory( logger logging.Logger, messageProtocolAddress common.Address, messageProtocolConfig config.MessageProtocolConfig, - destinationClients map[ids.ID]vms.DestinationClient, -) (*factory, error) { +) (messages.MessageHandlerFactory, error) { // Marshal the map and unmarshal into the Teleporter config data, err := json.Marshal(messageProtocolConfig.Settings) if err != nil { @@ -64,10 +62,9 @@ func NewMessageHandlerFactory( } return &factory{ - messageConfig: messageConfig, - protocolAddress: messageProtocolAddress, - destinationClients: destinationClients, - logger: logger, + messageConfig: messageConfig, + protocolAddress: messageProtocolAddress, + logger: logger, }, nil } @@ -121,14 +118,8 @@ func (m *messageHandler) GetMessageRoutingInfo() ( } // ShouldSendMessage returns true if the message should be sent to the destination chain -func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool, error) { - // Get the correct destination client from the global map - destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] - if !ok { - // This shouldn't occur, since the destination client map and message handler factories are built from the same configuration. - return false, fmt.Errorf("relayer not configured to deliver to destination. destinationBlockchainID=%s", destinationBlockchainID.String()) - } - +func (m *messageHandler) ShouldSendMessage(destinationClient vms.DestinationClient) (bool, error) { + destinationBlockchainID := destinationClient.DestinationBlockchainID() teleporterMessageID, err := teleporterUtils.CalculateMessageID( m.factory.protocolAddress, m.unsignedMessage.SourceChainID, @@ -151,7 +142,7 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool } // Check if the message has already been delivered to the destination chain - teleporterMessenger := m.factory.getTeleporterMessenger(destinationBlockchainID) + teleporterMessenger := m.factory.getTeleporterMessenger(destinationClient) delivered, err := teleporterMessenger.MessageReceived(&bind.CallOpts{}, teleporterMessageID) if err != nil { m.logger.Error( @@ -177,13 +168,8 @@ func (m *messageHandler) ShouldSendMessage(destinationBlockchainID ids.ID) (bool // 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, destinationBlockchainID ids.ID) error { - // Get the correct destination client from the global map - destinationClient, ok := m.factory.destinationClients[destinationBlockchainID] - if !ok { - return fmt.Errorf("relayer not configured to deliver to destination. DestinationBlockchainID=%s", destinationBlockchainID) - } - +func (m *messageHandler) SendMessage(signedMessage *warp.Message, destinationClient vms.DestinationClient) error { + destinationBlockchainID := destinationClient.DestinationBlockchainID() teleporterMessageID, err := teleporterUtils.CalculateMessageID( m.factory.protocolAddress, signedMessage.SourceChainID, @@ -278,11 +264,7 @@ 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(destinationBlockchainID ids.ID) *teleportermessenger.TeleporterMessenger { - destinationClient, ok := f.destinationClients[destinationBlockchainID] - if !ok { - return nil - } +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())) diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 032402bc..292d53d5 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -27,6 +27,7 @@ import ( "github.com/ava-labs/awm-relayer/peers" "github.com/ava-labs/awm-relayer/relayer/checkpoint" "github.com/ava-labs/awm-relayer/utils" + "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" warpBackend "github.com/ava-labs/subnet-evm/warp" @@ -63,6 +64,7 @@ type ApplicationRelayer struct { messageCreator message.Creator sourceBlockchain config.SourceBlockchain signingSubnetID ids.ID + destinationClient vms.DestinationClient relayerID database.RelayerID warpQuorum config.WarpQuorum checkpointManager *checkpoint.CheckpointManager @@ -78,6 +80,7 @@ func NewApplicationRelayer( relayerID database.RelayerID, db database.RelayerDatabase, ticker *utils.Ticker, + destinationClient vms.DestinationClient, sourceBlockchain config.SourceBlockchain, startingHeight uint64, cfg *config.Config, @@ -111,6 +114,7 @@ func NewApplicationRelayer( network: network, messageCreator: messageCreator, sourceBlockchain: sourceBlockchain, + destinationClient: destinationClient, relayerID: relayerID, signingSubnetID: signingSubnet, warpQuorum: quorum, @@ -125,12 +129,12 @@ 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 cosntructor -func (r *ApplicationRelayer) ProcessHeight(height uint64, messages []messages.MessageHandler) error { +func (r *ApplicationRelayer) ProcessHeight(height uint64, handlers []messages.MessageHandler) error { var eg errgroup.Group - for _, message := range messages { - m := message + for _, handler := range handlers { + h := handler eg.Go(func() error { - return r.ProcessMessage(m) + return r.ProcessMessage(h) }) } if err := eg.Wait(); err != nil { @@ -148,13 +152,13 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, messages []messages.Me zap.Uint64("height", height), zap.String("sourceBlockchainID", r.relayerID.SourceBlockchainID.String()), zap.String("relayerID", r.relayerID.ID.String()), - zap.Int("numMessages", len(messages)), + zap.Int("numMessages", len(handlers)), ) return nil } // Relays a message to the destination chain. Does not checkpoint the height. -func (r *ApplicationRelayer) ProcessMessage(msg messages.MessageHandler) error { +func (r *ApplicationRelayer) ProcessMessage(handler messages.MessageHandler) error { // Increment the request ID. Make sure we don't hold the lock while we relay the message. r.lock.Lock() r.currentRequestID++ @@ -163,7 +167,7 @@ func (r *ApplicationRelayer) ProcessMessage(msg messages.MessageHandler) error { err := r.relayMessage( reqID, - msg, + handler, true, ) @@ -176,7 +180,7 @@ func (r *ApplicationRelayer) RelayerID() database.RelayerID { func (r *ApplicationRelayer) relayMessage( requestID uint32, - messageHandler messages.MessageHandler, + handler messages.MessageHandler, useAppRequestNetwork bool, ) error { r.logger.Debug( @@ -185,7 +189,7 @@ func (r *ApplicationRelayer) relayMessage( zap.String("sourceBlockchainID", r.sourceBlockchain.BlockchainID), zap.String("relayerID", r.relayerID.ID.String()), ) - shouldSend, err := messageHandler.ShouldSendMessage(r.relayerID.DestinationBlockchainID) + shouldSend, err := handler.ShouldSendMessage(r.destinationClient) if err != nil { r.logger.Error( "Failed to check if message should be sent", @@ -198,7 +202,7 @@ func (r *ApplicationRelayer) relayMessage( r.logger.Info("Message should not be sent") return nil } - unsignedMessage := messageHandler.GetUnsignedMessage() + unsignedMessage := handler.GetUnsignedMessage() startCreateSignedMessageTime := time.Now() // Query nodes on the origin chain for signatures, and construct the signed warp message. @@ -228,7 +232,7 @@ func (r *ApplicationRelayer) relayMessage( // create signed message latency (ms) r.setCreateSignedMessageLatencyMS(float64(time.Since(startCreateSignedMessageTime).Milliseconds())) - err = messageHandler.SendMessage(signedMessage, r.relayerID.DestinationBlockchainID) + err = handler.SendMessage(signedMessage, r.destinationClient) if err != nil { r.logger.Error( "Failed to send warp message", diff --git a/relayer/listener.go b/relayer/listener.go index a9df5dd0..95f42997 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -51,7 +51,6 @@ type Listener struct { func NewListener( logger logging.Logger, sourceBlockchain config.SourceBlockchain, - destinationClients map[ids.ID]vms.DestinationClient, relayerHealth *atomic.Bool, cfg *config.Config, applicationRelayers map[common.Hash]*ApplicationRelayer, @@ -97,13 +96,11 @@ func NewListener( logger, address, cfg, - destinationClients, ) case config.OFF_CHAIN_REGISTRY: m, err = offchainregistry.NewMessageHandlerFactory( logger, cfg, - destinationClients, ) default: m, err = nil, fmt.Errorf("invalid message format %s", format) From 8ed636b65e4c0c0901d680c4a407fe4f27c505d5 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Wed, 22 May 2024 15:42:08 -0500 Subject: [PATCH 42/49] fix unit test --- messages/off-chain-registry/message_handler_test.go | 12 ++++-------- messages/teleporter/message_handler.go | 2 +- messages/teleporter/message_handler_test.go | 12 ++++-------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/messages/off-chain-registry/message_handler_test.go b/messages/off-chain-registry/message_handler_test.go index fccfa784..a71b895f 100644 --- a/messages/off-chain-registry/message_handler_test.go +++ b/messages/off-chain-registry/message_handler_test.go @@ -13,7 +13,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/awm-relayer/config" - "github.com/ava-labs/awm-relayer/vms" mock_evm "github.com/ava-labs/awm-relayer/vms/evm/mocks" mock_vms "github.com/ava-labs/awm-relayer/vms/mocks" teleporterregistry "github.com/ava-labs/teleporter/abi-bindings/go/Teleporter/upgrades/TeleporterRegistry" @@ -129,14 +128,10 @@ func TestShouldSendMessage(t *testing.T) { logger := logging.NoLog{} mockClient := mock_vms.NewMockDestinationClient(ctrl) - destinationClients := map[ids.ID]vms.DestinationClient{ - test.destinationBlockchainID: mockClient, - } - messageManager, err := NewMessageHandlerFactory( + factory, err := NewMessageHandlerFactory( logger, messageProtocolConfig, - destinationClients, ) require.NoError(t, err) ethClient := mock_evm.NewMockClient(ctrl) @@ -144,6 +139,7 @@ func TestShouldSendMessage(t *testing.T) { Client(). Return(ethClient). Times(test.clientTimes) + mockClient.EXPECT().DestinationBlockchainID().Return(test.destinationBlockchainID).AnyTimes() if test.getAddressFromVersionCall != nil { output, err := packGetAddressFromVersionOutput(test.getAddressFromVersionCall.expectedAddress) require.NoError(t, err) @@ -160,9 +156,9 @@ func TestShouldSendMessage(t *testing.T) { } else { unsignedMessage = createRegistryUnsignedWarpMessage(t, test.entry, teleporterRegistryAddress, test.destinationBlockchainID) } - messageHandler, err := messageManager.NewMessageHandler(unsignedMessage) + messageHandler, err := factory.NewMessageHandler(unsignedMessage) require.NoError(t, err) - result, err := messageHandler.ShouldSendMessage(test.destinationBlockchainID) + result, err := messageHandler.ShouldSendMessage(mockClient) if test.expectedError { require.Error(t, err) } else { diff --git a/messages/teleporter/message_handler.go b/messages/teleporter/message_handler.go index 25acd199..0ae086ff 100644 --- a/messages/teleporter/message_handler.go +++ b/messages/teleporter/message_handler.go @@ -273,7 +273,7 @@ func (f *factory) getTeleporterMessenger(destinationClient vms.DestinationClient // Get the teleporter messenger contract teleporterMessenger, err := teleportermessenger.NewTeleporterMessenger(f.protocolAddress, client) if err != nil { - panic(fmt.Sprintf("Failed to get teleporter messenger contract: %w", err)) + panic(fmt.Sprintf("Failed to get teleporter messenger contract: %s", err.Error())) } return teleporterMessenger } diff --git a/messages/teleporter/message_handler_test.go b/messages/teleporter/message_handler_test.go index 31a6238a..00557b49 100644 --- a/messages/teleporter/message_handler_test.go +++ b/messages/teleporter/message_handler_test.go @@ -12,7 +12,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/warp" warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/awm-relayer/config" - "github.com/ava-labs/awm-relayer/vms" mock_evm "github.com/ava-labs/awm-relayer/vms/evm/mocks" mock_vms "github.com/ava-labs/awm-relayer/vms/mocks" "github.com/ava-labs/subnet-evm/accounts/abi/bind" @@ -166,18 +165,14 @@ func TestShouldSendMessage(t *testing.T) { logger := logging.NoLog{} mockClient := mock_vms.NewMockDestinationClient(ctrl) - destinationClients := map[ids.ID]vms.DestinationClient{ - test.destinationBlockchainID: mockClient, - } - messageManager, err := NewMessageHandlerFactory( + factory, err := NewMessageHandlerFactory( logger, messageProtocolAddress, messageProtocolConfig, - destinationClients, ) require.NoError(t, err) - messageHandler, err := messageManager.NewMessageHandler(test.warpUnsignedMessage) + messageHandler, err := factory.NewMessageHandler(test.warpUnsignedMessage) if test.expectedParseError { // If we expect an error parsing the Warp message, we should not call ShouldSendMessage require.Error(t, err) @@ -194,6 +189,7 @@ func TestShouldSendMessage(t *testing.T) { SenderAddress(). Return(test.senderAddressResult). Times(test.senderAddressTimes) + mockClient.EXPECT().DestinationBlockchainID().Return(destinationBlockchainID).AnyTimes() if test.messageReceivedCall != nil { messageReceivedInput := interfaces.CallMsg{ From: bind.CallOpts{}.From, @@ -206,7 +202,7 @@ func TestShouldSendMessage(t *testing.T) { Times(test.messageReceivedCall.times) } - result, err := messageHandler.ShouldSendMessage(test.destinationBlockchainID) + result, err := messageHandler.ShouldSendMessage(mockClient) require.NoError(t, err) require.Equal(t, test.expectedResult, result) }) From d1936ec190f8c4b5642617ce98c63bc36f55617d Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Thu, 23 May 2024 09:57:51 -0500 Subject: [PATCH 43/49] handle app relayer errors --- relayer/application_relayer.go | 6 +++--- relayer/listener.go | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 29c869f3..ccfa34fd 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -129,7 +129,7 @@ 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) error { +func (r *ApplicationRelayer) ProcessHeight(height uint64, handlers []messages.MessageHandler, errChan chan error) { var eg errgroup.Group for _, handler := range handlers { h := handler @@ -144,7 +144,8 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, handlers []messages.Me zap.String("relayerID", r.relayerID.ID.String()), zap.Error(err), ) - return err + errChan <- err + return } r.checkpointManager.StageCommittedHeight(height) r.logger.Debug( @@ -154,7 +155,6 @@ func (r *ApplicationRelayer) ProcessHeight(height uint64, handlers []messages.Me zap.String("relayerID", r.relayerID.ID.String()), zap.Int("numMessages", len(handlers)), ) - return nil } // Relays a message to the destination chain. Does not checkpoint the height. diff --git a/relayer/listener.go b/relayer/listener.go index 95f42997..59f7e548 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -176,8 +176,16 @@ func NewListener( // 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 { + // Error channel for application relayer errors + errChan := make(chan error) for { select { + case err := <-errChan: + lstnr.healthStatus.Store(false) + lstnr.logger.Error( + "Received error from application relayer", + zap.Error(err), + ) case catchUpResult, ok := <-lstnr.catchUpResultChan: // As soon as we've received anything on the channel, there are no more values expected. // The expected case is that the channel is closed by the subscriber after writing a value to it, @@ -246,7 +254,7 @@ func (lstnr *Listener) ProcessLogs(ctx context.Context) error { // 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) + go appRelayer.ProcessHeight(block.BlockNumber, handlers, errChan) } case err := <-lstnr.Subscriber.Err(): lstnr.healthStatus.Store(false) From 59210e03486913072eb8acf031898ca10586ba25 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Thu, 23 May 2024 09:58:11 -0500 Subject: [PATCH 44/49] add upgrade comment --- relayer/application_relayer.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index ccfa34fd..31d0f78c 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -132,6 +132,8 @@ func NewApplicationRelayer( 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 h := handler eg.Go(func() error { return r.ProcessMessage(h) From f4f44e6b8c3be0a1e811d12bc004d039cfeb3054 Mon Sep 17 00:00:00 2001 From: cam-schultz Date: Thu, 23 May 2024 13:06:30 -0500 Subject: [PATCH 45/49] only lock nonce usage --- vms/evm/destination_client.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/vms/evm/destination_client.go b/vms/evm/destination_client.go index 9878f9dc..2ad59d5d 100644 --- a/vms/evm/destination_client.go +++ b/vms/evm/destination_client.go @@ -114,10 +114,6 @@ func (c *destinationClient) SendTx(signedMessage *avalancheWarp.Message, gasLimit uint64, callData []byte, ) error { - // Synchronize teleporter message requests to the same destination chain so that message ordering is preserved - c.lock.Lock() - defer c.lock.Unlock() - // Get the current base fee estimation, which is based on the previous blocks gas usage. baseFee, err := c.client.EstimateBaseFee(context.Background()) if err != nil { @@ -143,6 +139,12 @@ func (c *destinationClient) SendTx(signedMessage *avalancheWarp.Message, gasFeeCap := baseFee.Mul(baseFee, big.NewInt(BaseFeeFactor)) gasFeeCap.Add(gasFeeCap, big.NewInt(MaxPriorityFeePerGas)) + // Synchronize nonce access so that we send transactions in nonce order. + // Hold the lock until the transaction is sent to minimize the chance of + // an out-of-order transaction being dropped from the mempool. + c.lock.Lock() + defer c.lock.Unlock() + // Construct the actual transaction to broadcast on the destination chain tx := predicateutils.NewPredicateTx( c.evmChainID, @@ -175,9 +177,6 @@ func (c *destinationClient) SendTx(signedMessage *avalancheWarp.Message, ) return err } - - // Increment the nonce to use on the destination chain now that we've sent - // a transaction using the current value. c.currentNonce++ c.logger.Info( "Sent transaction", From 1782f31fd35f2a9b5fcda43965ebeca0e8527637 Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 31 May 2024 09:12:09 -0400 Subject: [PATCH 46/49] Refactor application relayers --- main/main.go | 107 ++++++++++++++++------------------ relayer/listener.go | 4 +- vms/evm/destination_client.go | 8 ++- 3 files changed, 59 insertions(+), 60 deletions(-) diff --git a/main/main.go b/main/main.go index ff2a9093..58995590 100644 --- a/main/main.go +++ b/main/main.go @@ -234,27 +234,67 @@ func main() { ) panic(err) } - subnetInfo := s + sourceBlockchain := s health := atomic.NewBool(true) relayerHealth[blockchainID] = health // 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( + // 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, - *subnetInfo, + *sourceBlockchain, network, - destinationClients, 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), + ) + + // runListener runs until it errors or the context is cancelled by another goroutine + return runListener( + ctx, + logger, + *sourceBlockchain, health, manualWarpMessages[blockchainID], &cfg, + ethClient, + applicationRelayers, + minHeight, ) }) } @@ -270,66 +310,20 @@ func main() { func runListener( ctx context.Context, logger logging.Logger, - metrics *relayer.ApplicationRelayerMetrics, - db database.RelayerDatabase, - ticker *utils.Ticker, sourceBlockchain config.SourceBlockchain, - network *peers.AppRequestNetwork, - destinationClients map[ids.ID]vms.DestinationClient, - messageCreator message.Creator, relayerHealth *atomic.Bool, manualWarpMessages []*relayerTypes.WarpMessageInfo, - cfg *config.Config, + globalConfig *config.Config, + ethClient ethclient.Client, + applicationRelayers map[common.Hash]*relayer.ApplicationRelayer, + minHeight uint64, ) 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), - ) - // Create the Listener listener, err := relayer.NewListener( logger, sourceBlockchain, relayerHealth, - cfg, + globalConfig, applicationRelayers, minHeight, ethClient, @@ -360,6 +354,7 @@ func runListener( return listener.ProcessLogs(ctx) } +// createApplicationRelayers creates Application Relayers for a given source blockchain. func createApplicationRelayers( ctx context.Context, logger logging.Logger, diff --git a/relayer/listener.go b/relayer/listener.go index 59f7e548..57449ddc 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -52,7 +52,7 @@ func NewListener( logger logging.Logger, sourceBlockchain config.SourceBlockchain, relayerHealth *atomic.Bool, - cfg *config.Config, + globalConfig *config.Config, applicationRelayers map[common.Hash]*ApplicationRelayer, startingHeight uint64, ethClient ethclient.Client, @@ -140,7 +140,7 @@ func NewListener( sourceBlockchain: sourceBlockchain, catchUpResultChan: catchUpResultChan, healthStatus: relayerHealth, - globalConfig: cfg, + globalConfig: globalConfig, applicationRelayers: applicationRelayers, ethClient: ethClient, } diff --git a/vms/evm/destination_client.go b/vms/evm/destination_client.go index 2ad59d5d..354ea689 100644 --- a/vms/evm/destination_client.go +++ b/vms/evm/destination_client.go @@ -46,7 +46,10 @@ type destinationClient struct { logger logging.Logger } -func NewDestinationClient(logger logging.Logger, destinationBlockchain *config.DestinationBlockchain) (*destinationClient, error) { +func NewDestinationClient( + logger logging.Logger, + destinationBlockchain *config.DestinationBlockchain, +) (*destinationClient, error) { // Dial the destination RPC endpoint client, err := ethclient.DialWithConfig( context.Background(), @@ -109,7 +112,8 @@ func NewDestinationClient(logger logging.Logger, destinationBlockchain *config.D }, nil } -func (c *destinationClient) SendTx(signedMessage *avalancheWarp.Message, +func (c *destinationClient) SendTx( + signedMessage *avalancheWarp.Message, toAddress string, gasLimit uint64, callData []byte, From ba2f686e9ef9288d7ec8d9f6350849834ea3074a Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 31 May 2024 09:53:10 -0400 Subject: [PATCH 47/49] Fix typo --- database/relayer_id.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/relayer_id.go b/database/relayer_id.go index 50cc692f..e1c0d6b1 100644 --- a/database/relayer_id.go +++ b/database/relayer_id.go @@ -53,7 +53,7 @@ func CalculateRelayerID( sourceBlockchainID ids.ID, destinationBlockchainID ids.ID, originSenderAddress common.Address, - desinationAddress common.Address, + destinationAddress common.Address, ) common.Hash { return crypto.Keccak256Hash( []byte(strings.Join( @@ -61,7 +61,7 @@ func CalculateRelayerID( sourceBlockchainID.String(), destinationBlockchainID.String(), originSenderAddress.String(), - desinationAddress.String(), + destinationAddress.String(), }, "-", )), From 65b8ba85eb66f0b2f92483e4afa5bd6a01aef1bb Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 31 May 2024 13:54:11 -0400 Subject: [PATCH 48/49] Fix key truncation --- tests/utils/utils.go | 4 ++-- utils/utils.go | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/utils/utils.go b/tests/utils/utils.go index abab1e78..ea337288 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -7,7 +7,6 @@ import ( "bufio" "context" "crypto/ecdsa" - "encoding/hex" "encoding/json" "fmt" "math/big" @@ -20,6 +19,7 @@ import ( avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/awm-relayer/config" + relayerUtils "github.com/ava-labs/awm-relayer/utils" offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" batchcrosschainmessenger "github.com/ava-labs/awm-relayer/tests/abi-bindings/go/BatchCrossChainMessenger" "github.com/ava-labs/subnet-evm/accounts/abi/bind" @@ -167,7 +167,7 @@ func CreateDefaultRelayerConfig( RPCEndpoint: config.APIConfig{ BaseURL: fmt.Sprintf("http://%s:%d/ext/bc/%s/rpc", host, port, subnetInfo.BlockchainID.String()), }, - AccountPrivateKey: hex.EncodeToString(relayerKey.D.Bytes()), + AccountPrivateKey: relayerUtils.PrivateKeyToString(relayerKey), } log.Info( diff --git a/utils/utils.go b/utils/utils.go index 354f5460..17feeb49 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -4,6 +4,8 @@ package utils import ( + "crypto/ecdsa" + "encoding/hex" "errors" "math/big" "strings" @@ -58,6 +60,11 @@ func BigToHashSafe(in *big.Int) (common.Hash, error) { return common.BytesToHash(bytes), nil } +func PrivateKeyToString(key *ecdsa.PrivateKey) string { + // Use FillBytes so leading zeroes are not stripped. + return hex.EncodeToString(key.D.FillBytes(make([]byte, 32))) +} + // SanitizeHexString removes the "0x" prefix from a hex string if it exists. // Otherwise, returns the original string. func SanitizeHexString(hex string) string { From 3404bf76c7fbe3c5b2ea649aab2ee234f279f06d Mon Sep 17 00:00:00 2001 From: Geoff Stuart Date: Fri, 31 May 2024 13:58:05 -0400 Subject: [PATCH 49/49] lint --- tests/utils/utils.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/utils/utils.go b/tests/utils/utils.go index ea337288..bb11c47b 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -19,9 +19,9 @@ import ( avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" "github.com/ava-labs/awm-relayer/config" - relayerUtils "github.com/ava-labs/awm-relayer/utils" offchainregistry "github.com/ava-labs/awm-relayer/messages/off-chain-registry" batchcrosschainmessenger "github.com/ava-labs/awm-relayer/tests/abi-bindings/go/BatchCrossChainMessenger" + relayerUtils "github.com/ava-labs/awm-relayer/utils" "github.com/ava-labs/subnet-evm/accounts/abi/bind" "github.com/ava-labs/subnet-evm/core/types" "github.com/ava-labs/subnet-evm/precompile/contracts/warp" @@ -41,8 +41,10 @@ import ( // Write the test database to /tmp since the data is not needed after the test var StorageLocation = fmt.Sprintf("%s/.awm-relayer-storage", os.TempDir()) -const DefaultRelayerCfgFname = "relayer-config.json" -const DBUpdateSeconds = 1 +const ( + DefaultRelayerCfgFname = "relayer-config.json" + DBUpdateSeconds = 1 +) func BuildAndRunRelayerExecutable(ctx context.Context, relayerConfigPath string) context.CancelFunc { // Build the awm-relayer binary