-
Notifications
You must be signed in to change notification settings - Fork 19
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
Conversation
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"` |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
zap.String("warpMessageID", warpMessageInfo.WarpUnsignedMessage.ID().String()), | ||
zap.String("teleporterMessageID", teleporterMessage.MessageID.String()), | ||
) | ||
return false, nil |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved 👍
There was a problem hiding this 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.
@@ -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 |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
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
s.SubnetID, | ||
blockchainID) | ||
} | ||
s.supportedDestinations.Add(chainID) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this 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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Why this should be merged
source subnets
todestination subnets
AllowedDestinations
chainIDs insource subnets
to specify the allowed destinations.How this was tested