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

Configuration support fully specified source to destination mapping #59

Merged
merged 34 commits into from
Oct 27, 2023

Conversation

gwen917
Copy link
Contributor

@gwen917 gwen917 commented Oct 13, 2023

Why this should be merged

  • Allow relayer only relays the message between specific subnets, instead of relaying all the messages between source subnets to destination subnets
  • Add AllowedDestinations chainIDs in source subnets to specify the allowed destinations.

How this was tested

  • Unit tests, CI

config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
messages/teleporter/message_manager.go Outdated Show resolved Hide resolved
messages/teleporter/message_manager.go Outdated Show resolved Hide resolved
messages/teleporter/message_manager.go Outdated Show resolved Hide resolved
@gwen917 gwen917 linked an issue Oct 16, 2023 that may be closed by this pull request
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated
WSEndpoint string `mapstructure:"ws-endpoint" json:"ws-endpoint"`
MessageContracts map[string]MessageProtocolConfig `mapstructure:"message-contracts" json:"message-contracts"`
SubnetID string `mapstructure:"subnet-id" json:"subnet-id"`
ChainID string `mapstructure:"chain-id" json:"chain-id"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this "ChainID" the referring to the EVM chain ID, or the Avalanche-specific blockchain ID?

@cam-schultz may know best here, but if it's the blockchain ID, lets rename it to avoid future confusion.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, this is the Avalanche blockchain ID.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is an unfortunate overloading of terms, but I agree we should make the distinction where we can. FWIW, subnet-evm isn't consistent either: ethclient.Client.ChainID returns the blockchainID, while the Warp precompile has the unambiguous method getBlockchainID. I don't think the EVM chain ID comes into play in subnet-evm or awm-relayer.

There are 479 hits for chainID in this repo, however... Do you mind if we save this for a separate PR to not pollute the diff?

Copy link

@minghinmatthewlam minghinmatthewlam Oct 25, 2023

Choose a reason for hiding this comment

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

Similar to the above ambiguity in naming, DevRel brought up the confusion of destinationChainID in TeleporterMessenger, should we change try to change all references of chainID in favor of blockchainID so it'd be destinationBlockchainID? Is this enough of a differentiation for people to not get confused with EVM chainID?

Copy link
Contributor

Choose a reason for hiding this comment

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

afaik ethclient.Client.ChainID returns the eth chainID. I don't think the eth client has any way of knowing the blockchainID.

Also note that in the go code, chainID is a big.Int and blockchainID is a [32]byte

Copy link
Collaborator

Choose a reason for hiding this comment

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

Created an issue here: #70

config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
messages/teleporter/message_manager.go Outdated Show resolved Hide resolved
zap.String("warpMessageID", warpMessageInfo.WarpUnsignedMessage.ID().String()),
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()),
)
return false, nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the case above where the destination chain ID does not have a destination client, we return false and a non-nil error. I noticed this case is different in that we return false, nil. Inspecting the code, it looks like it's incorrect to return an error in the case above (unless I'm missing another check on whether or not the destination chain ID is supported elsewhere).

@cam-schultz could you confirm this?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, that case should not be an error. With the upcoming subnet-evm changes (as well as anycasting support in the future), I think ShouldSendMessage is the right place to check against the list of configured destinations.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thinking about it some more, we can clean up this implementation by using the configured allowed destinations as a filter in on the list of destination clients in NewRelayer before the call to NewMessageManager. We'd then pass in the filtered list of destination clients. In relayer.go, we'd add something like:

var filteredDestinationClients map[ids.ID]ethclient.Client
allowedDestinationChainIDs, err := sourceSubnetInfo.GetAllowedDestinations()
if len(m.allowedDestinations) > 0 { 
    filteredDestinationClients := make(map[ids.ID]ethclient.Client)
    for _, id := range m.allowedDestinations {
        filteredDestinationClients[id] = destinationClients[id]
    }
} else {
    filteredDestinationClients = destinationClients
}
...
messageManager, err := messages.NewMessageManager(logger, addressHash, config, filteredDestinationClients, allowedDestinationChainIDs)

This would move logic for deciding which destinations are and are not valid to the relayer constructor which ones once, rather than the message manager ShouldSendMessage implementation, which runs for each message. When processing a message log, we could then reference the allowed destinations for that source subnet here

Choose a reason for hiding this comment

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

would agree that we should move the destination client filtering logic to NewRelayer to prevent checking each message.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Moved 👍

Copy link
Contributor

@bernard-avalabs bernard-avalabs left a comment

Choose a reason for hiding this comment

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

Generally makes sense to me. I left a few minor comments.

messages/teleporter/message_manager.go Outdated Show resolved Hide resolved
@@ -105,6 +108,22 @@ func (m *messageManager) ShouldSendMessage(warpMessageInfo *vmtypes.WarpMessageI
if !ok {
return false, fmt.Errorf("relayer not configured to deliver to destination. destinationChainID=%s", destinationChainID.String())
}

// If supportedDestinationss is empty, then all destinations are allowed
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems opposite than the expected behavior (one normally assumes that if the allow list is empty, then nothing is allowed).

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is similar to how the "allowedRelayerAddresses" field is handled in TeleporterMessenger: https://github.com/ava-labs/teleporter/blob/main/contracts/src/Teleporter/TeleporterMessenger.sol#L126

The expected use case here is that if the supportedDestinations JSON key is not provided, we don't filter any of the configured destinations. This would be useful if the relayer operator wants messages from subnet A to be relayed to anybody, but messages from subnet B to only be relayed to subnet C, for example.

@@ -25,6 +25,85 @@ The relayer binary accepts a path to a JSON configuration file as the sole argum
./build/awm-relayer --config-file path-to-config
```

### Configuration

Choose a reason for hiding this comment

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

Love this new README section 🙌

config/config.go Outdated
//

// GetSourceIDs returns the Subnet and Chain IDs of all subnets configured as a source
func GetSourceIDs() ([]ids.ID, []ids.ID, error) {

Choose a reason for hiding this comment

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

why did we move this to using a global config variable vs a receiver function?

Copy link
Collaborator

Choose a reason for hiding this comment

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

After it's constructed, the configuration is a static singleton, so this pattern makes access more straightforward. The alternative is passing the option from the config down the call stack to where it's used, unnecessarily complicating interfaces along the way.

Choose a reason for hiding this comment

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

I have a preference, though not a strong preference, to stay away from the global variables and to instead pass in the configs, similar to previously how we had global var for destination clients. This also helps prevent in the future from accidentally ovewriting values in the global config

Copy link
Collaborator

Choose a reason for hiding this comment

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

On second thought, I agree we should pass the config down the callstack rather than have a global variable. I've made this change.

config/config.go Outdated Show resolved Hide resolved
@cam-schultz
Copy link
Collaborator

mostly LGTM, left some questions. If we have a case of source chains A,B,C, and destiantion chains D,E,F. Now we want to add chain G as an allowed destination for only A, we'd also have to add the allow destiantion field of DEF for chains B and C right? Since the chains B and C would otherwise deliver to chain G automatically.

Yes, that's correct. If no supported destinations are listed, all configured destination subnets are implicitly allowed. Otherwise, to disallow just a single destination, all other destinations must be listed.

config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated
s.SubnetID,
blockchainID)
}
s.supportedDestinations.Add(chainID)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd consider moving setting the supportedDestinations value to a initialize method to be more obvious, but no strong preference either way

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that might be more trouble than it's worth. To do so properly, we'd want to iterate through all supported destinations in the validate method, then do so again to cache them in initialize. Here, we do it in a single pass.

config/config.go Outdated Show resolved Hide resolved
relayer/relayer.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@michaelkaplan13 michaelkaplan13 left a comment

Choose a reason for hiding this comment

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

One small comment and one question, but otherwise LGTM

relayer/relayer_test.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
config/config.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@michaelkaplan13 michaelkaplan13 left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@geoff-vball geoff-vball left a comment

Choose a reason for hiding this comment

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

LGTM!

@cam-schultz cam-schultz merged commit d79025f into main Oct 27, 2023
7 checks passed
@cam-schultz cam-schultz deleted the p2p-config branch October 27, 2023 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Configuration support fully specified source to destination mapping
6 participants