Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Teleporter message id changes #128

Merged
merged 3 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/ava-labs/avalanchego v1.10.17
github.com/ava-labs/coreth v0.12.9-rc.9
github.com/ava-labs/subnet-evm v0.5.10
github.com/ava-labs/teleporter v0.0.0-20231221165433-826fa59bed3c
github.com/ava-labs/teleporter v0.0.0-20240108172200-f03f526e5312
github.com/ethereum/go-ethereum v1.12.0
github.com/onsi/ginkgo/v2 v2.13.2
github.com/onsi/gomega v1.30.0
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ github.com/ava-labs/subnet-evm v0.5.10 h1:ed9BxoiuXRnB/qKakKzYKtZzV/gVjOB2LxuDeg
github.com/ava-labs/subnet-evm v0.5.10/go.mod h1:wln8B4siQ1Osch+elW9vW1XJGjj5PYxQETkzFyDEMjk=
github.com/ava-labs/teleporter v0.0.0-20231221165433-826fa59bed3c h1:vnMlfP4SHFoatRufgUma/eGwvVzWdwMo17ADdrh6YYQ=
github.com/ava-labs/teleporter v0.0.0-20231221165433-826fa59bed3c/go.mod h1:qeclhkPTO4R2McXNrXXca4JmiRSgQ0gJ0KtJWzQGGPE=
github.com/ava-labs/teleporter v0.0.0-20240104215757-839006a992f2 h1:gqO87g7c6Gy5ZjEzz/oQ3KgnGqvdy73hypnQ+gvmTbQ=
github.com/ava-labs/teleporter v0.0.0-20240104215757-839006a992f2/go.mod h1:qeclhkPTO4R2McXNrXXca4JmiRSgQ0gJ0KtJWzQGGPE=
github.com/ava-labs/teleporter v0.0.0-20240105220309-160c7b8bce4b h1:tBUZfmBqdAykjTcL771SZcE5iQ5kgRZ6nz9Q/VQNTK0=
github.com/ava-labs/teleporter v0.0.0-20240105220309-160c7b8bce4b/go.mod h1:qeclhkPTO4R2McXNrXXca4JmiRSgQ0gJ0KtJWzQGGPE=
github.com/ava-labs/teleporter v0.0.0-20240105221051-581342d9f521 h1:rHbDNvhen/qDQyOuFkdkQ91MjPSNOcmbrUtaTEjriH8=
github.com/ava-labs/teleporter v0.0.0-20240105221051-581342d9f521/go.mod h1:qeclhkPTO4R2McXNrXXca4JmiRSgQ0gJ0KtJWzQGGPE=
github.com/ava-labs/teleporter v0.0.0-20240108172200-f03f526e5312 h1:rG9xkvCXRU4FBi1IBIXPxnTwxRv7mM6j0PX7FQss32g=
github.com/ava-labs/teleporter v0.0.0-20240108172200-f03f526e5312/go.mod h1:qeclhkPTO4R2McXNrXXca4JmiRSgQ0gJ0KtJWzQGGPE=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
Expand Down
30 changes: 25 additions & 5 deletions messages/mocks/mock_message_manager.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

144 changes: 63 additions & 81 deletions messages/teleporter/message_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
package teleporter

import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"

"github.com/ava-labs/avalanchego/cache"
"github.com/ava-labs/avalanchego/ids"
Expand All @@ -16,8 +15,8 @@ import (
"github.com/ava-labs/awm-relayer/config"
"github.com/ava-labs/awm-relayer/vms"
"github.com/ava-labs/awm-relayer/vms/vmtypes"
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/ethclient"
"github.com/ava-labs/subnet-evm/interfaces"
teleportermessenger "github.com/ava-labs/teleporter/abi-bindings/go/Teleporter/TeleporterMessenger"
gasUtils "github.com/ava-labs/teleporter/utils/gas-utils"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -125,29 +124,31 @@ func (m *messageManager) ShouldSendMessage(warpMessageInfo *vmtypes.WarpMessageI
return false, fmt.Errorf("relayer not configured to deliver to destination. destinationBlockchainID=%s", destinationBlockchainID.String())
}

teleporterMessenger := m.getTeleporterMessenger(destinationClient)
teleporterMessageID, err := m.calculateMessageID(teleporterMessenger, warpMessageInfo.WarpUnsignedMessage.SourceChainID, destinationBlockchainID, teleporterMessage.MessageNonce)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It a bit sub-optimal that getting the message ID requires an additional API call here, but given the current interfaces and utilities, it's I agree that it's the easiest option.

Alternatively, the message ID is emitted as a part of the SendCrossChainMessage event and could be used directly from there (though we don't currently subscribe or otherwise get that event), or we could provide a Golang utility for the hash operation, which would be far more efficient than an API call but have a small amount of duplicated logic.

I don't think we need to change it for this PR, but wanted to call out to hear people's thoughts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah investigated this a bit and unfortunately there is no way to call any methods from the golang ABI without an RPC call, even if you're calling a pure function. I totally agree that needing the API here is very undesired.

I think the best solution here is probably to build our own Golang utility that reproduces the functionality. Importantly, we should add a test (or include the sanity check in the e2e network setup) that calls the Teleporter contract to make sure it aligns with our utility.

We can totally defer this to a new ticket, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the utility should be located in the teleporter repo.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created new issue to track: #136

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on all points. In the future when optimizing end to end relayer latency, minimizing the number of network round trips will be an obvious starting point.

if err != nil {
return false, err
}

senderAddress := destinationClient.SenderAddress()
if !isAllowedRelayer(teleporterMessage.AllowedRelayerAddresses, senderAddress) {
m.logger.Info(
"Relayer EOA not allowed to deliver this message.",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", warpMessageInfo.WarpUnsignedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
)
return false, nil
}

delivered, err := m.messageDelivered(
destinationClient,
warpMessageInfo,
teleporterMessage,
destinationBlockchainID,
)
// Check if the message has already been delivered to the destination chain
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", warpMessageInfo.WarpUnsignedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
zap.Error(err),
)
return false, err
Expand All @@ -156,7 +157,7 @@ func (m *messageManager) ShouldSendMessage(warpMessageInfo *vmtypes.WarpMessageI
m.logger.Info(
"Message already delivered to destination.",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
)
return false, nil
}
Expand All @@ -166,64 +167,6 @@ func (m *messageManager) ShouldSendMessage(warpMessageInfo *vmtypes.WarpMessageI
return true, nil
}

// Helper to check if a message has been delivered to the destination chain
// Returns true if the message has been delivered, false if not
// On error, the boolean result should be ignored
func (m *messageManager) messageDelivered(
destinationClient vms.DestinationClient,
warpMessageInfo *vmtypes.WarpMessageInfo,
teleporterMessage *teleportermessenger.TeleporterMessage,
destinationBlockchainID ids.ID) (bool, error) {
// Check if the message has already been delivered to the destination chain
client, ok := destinationClient.Client().(ethclient.Client)
if !ok {
m.logger.Error(
"Destination client is not an Ethereum client.",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
)
return false, errors.New("destination client is not an Ethereum client")
}

data, err := teleportermessenger.PackMessageReceived(
warpMessageInfo.WarpUnsignedMessage.SourceChainID,
teleporterMessage.MessageID,
)
if err != nil {
m.logger.Error(
"Failed packing messageReceived call data.",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.Error(err),
)
return false, err
}
protocolAddress := common.BytesToAddress(m.protocolAddress[:])
callMessage := interfaces.CallMsg{
To: &protocolAddress,
Data: data,
}
result, err := client.CallContract(context.Background(), callMessage, nil)
if err != nil {
m.logger.Error(
"Failed calling messageReceived method on destination chain.",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.Error(err),
)
return false, err
}
// check the contract call result
delivered, err := teleportermessenger.UnpackMessageReceivedResult(result)
if err != nil {
m.logger.Error(
"Failed unpacking messageReceived result.",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.Error(err),
)
return false, err
}

return delivered, nil
}

// SendMessage extracts the gasLimit and packs the call data to call the receiveCrossChainMessage method of the Teleporter contract,
// and dispatches transaction construction and broadcast to the destination client
func (m *messageManager) SendMessage(signedMessage *warp.Message, parsedVmPayload []byte, destinationBlockchainID ids.ID) error {
Expand All @@ -237,19 +180,31 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, parsedVmPayloa
return err
}

// Get the correct destination client from the global map
destinationClient, ok := m.destinationClients[destinationBlockchainID]
if !ok {
return fmt.Errorf("relayer not configured to deliver to destination. DestinationBlockchainID=%s", destinationBlockchainID)
}

teleporterMessenger := m.getTeleporterMessenger(destinationClient)
teleporterMessageID, err := m.calculateMessageID(teleporterMessenger, signedMessage.SourceChainID, destinationBlockchainID, teleporterMessage.MessageNonce)
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

m.logger.Info(
"Sending message to destination chain",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", signedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
)
numSigners, err := signedMessage.Signature.NumSigners()
if err != nil {
m.logger.Error(
"Failed to get number of signers",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", signedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
)
return err
}
Expand All @@ -259,7 +214,7 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, parsedVmPayloa
"Gas limit required overflowed uint64 max. not relaying message",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", signedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
)
return err
}
Expand All @@ -270,23 +225,18 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, parsedVmPayloa
"Failed packing receiveCrossChainMessage call data",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", signedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
)
return err
}

// Get the correct destination client from the global map
destinationClient, ok := m.destinationClients[destinationBlockchainID]
if !ok {
return fmt.Errorf("relayer not configured to deliver to destination. DestinationBlockchainID=%s", destinationBlockchainID)
}
err = destinationClient.SendTx(signedMessage, m.protocolAddress.Hex(), gasLimit, callData)
if err != nil {
m.logger.Error(
"Failed to send tx.",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", signedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
zap.Error(err),
)
return err
Expand All @@ -295,7 +245,7 @@ func (m *messageManager) SendMessage(signedMessage *warp.Message, parsedVmPayloa
"Sent message to destination chain",
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.String("warpMessageID", signedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
zap.String("teleporterMessageID", teleporterMessageID.String()),
)
return nil
}
Expand Down Expand Up @@ -323,3 +273,35 @@ func (m *messageManager) parseTeleporterMessage(warpMessageID ids.ID, warpPayloa
}
return teleporterMessage, nil
}

// 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 {
client, ok := destinationClient.Client().(ethclient.Client)
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
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(common.BytesToAddress(m.protocolAddress[:]), client)
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
panic("Failed to get teleporter messenger contract")
}
return teleporterMessenger
}

func (m *messageManager) calculateMessageID(teleporter *teleportermessenger.TeleporterMessenger, sourceBlockchainID ids.ID, destinationBlockchainID ids.ID, messageNonce *big.Int) (ids.ID, error) {
messageID, err := teleporter.CalculateMessageID(&bind.CallOpts{}, sourceBlockchainID, destinationBlockchainID, messageNonce)
if err != nil {
m.logger.Error(
"Failed to calculate message ID",
zap.String("sourceBlockchainID", sourceBlockchainID.String()),
zap.String("destinationBlockchainID", destinationBlockchainID.String()),
zap.Error(err),
)
return ids.Empty, err
}

return messageID, nil
}
Loading
Loading