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

use warpRequirePrimaryNetworkSigners config #537

Merged
merged 8 commits into from
Nov 22, 2024
17 changes: 14 additions & 3 deletions relayer/application_relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,21 @@ func NewApplicationRelayer(
)
return nil, err
}
requirePrimaryNetworkSigners, err := cfg.GetWarpRequirePrimaryNetworkSigners(relayerID.DestinationBlockchainID)
// this shouldn't be reachable since if we found a quorum, we should also find the requirePrimaryNetworkSigners
// but leaving the check for completeness
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that's probably an indication we should be able to fetch them from the config together?

I also agree with your comment that the current WarpQuorum object is unnecessary. We can just use a uint for QuorumPercentage, or have WarpQuorum contain the percentage and requirePrimaryNetworkSigners

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went with the latter route and renamed it to WarpConfig. It's basically identical to the source WarpConfig defined in subnet-evm but doesn't implement the Upgrade interface and when converting it we populate it with warpDefaultQuorumNumerator value if it's 0

if err != nil {
logger.Error(
"Failed to get warp primary network configuration from config.",
zap.String("destinationBlockchainID", relayerID.DestinationBlockchainID.String()),
zap.Error(err),
)
return nil, err
}
var signingSubnet ids.ID
if sourceBlockchain.GetSubnetID() == constants.PrimaryNetworkID {
// If the message originates from the primary subnet, then we instead "self sign"
// the message using the validators of the destination subnet.
if sourceBlockchain.GetSubnetID() == constants.PrimaryNetworkID && !requirePrimaryNetworkSigners {
// If the message originates from the primary subnet, and the primary network is validated by
// the destination change we can "self-sign" the message using the validators of the destination subnet.
iansuvak marked this conversation as resolved.
Show resolved Hide resolved
signingSubnet = cfg.GetSubnetID(relayerID.DestinationBlockchainID)
} else {
// Otherwise, the source subnet signs the message.
Expand Down
56 changes: 26 additions & 30 deletions relayer/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/ava-labs/awm-relayer/peers"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/set"

Expand Down Expand Up @@ -156,31 +155,25 @@ func (c *Config) GetSubnetID(blockchainID ids.ID) ids.ID {
}

// If the numerator in the Warp config is 0, use the default value
func calculateQuorumNumerator(cfgNumerator uint64) uint64 {
func calculateQuorum(cfgNumerator uint64) WarpQuorum {
if cfgNumerator == 0 {
return warp.WarpDefaultQuorumNumerator
}
return cfgNumerator
}

// Helper to retrieve the Warp Quorum from the chain config.
// Differentiates between subnet-evm and coreth RPC internally
func getWarpQuorum(
subnetID ids.ID,
blockchainID ids.ID,
client ethclient.Client,
) (WarpQuorum, error) {
if subnetID == constants.PrimaryNetworkID {
return WarpQuorum{
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
}, nil
}
} else {
return WarpQuorum{
QuorumNumerator: cfgNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
}
}
iansuvak marked this conversation as resolved.
Show resolved Hide resolved
}

func getWarpConfig(client ethclient.Client) (*warp.Config, error) {
// Fetch the subnet's chain config
chainConfig, err := client.ChainConfig(context.Background())
if err != nil {
return WarpQuorum{}, fmt.Errorf("failed to fetch chain config for blockchain %s: %w", blockchainID, err)
return nil, fmt.Errorf("failed to fetch chain config")
}

// First, check the list of precompile upgrades to get the most up to date Warp config
Expand All @@ -202,27 +195,21 @@ func getWarpQuorum(
}
}
if warpConfig != nil {
return WarpQuorum{
QuorumNumerator: calculateQuorumNumerator(warpConfig.QuorumNumerator),
QuorumDenominator: warp.WarpQuorumDenominator,
}, nil
return warpConfig, nil
}

// If we didn't find the Warp config in the upgrade precompile list, check the genesis config
warpConfig, ok := chainConfig.GenesisPrecompiles[warpConfigKey].(*warp.Config)
if ok {
return WarpQuorum{
QuorumNumerator: calculateQuorumNumerator(warpConfig.QuorumNumerator),
QuorumDenominator: warp.WarpQuorumDenominator,
}, nil
if !ok {
return nil, fmt.Errorf("no Warp config found in chain config")
}
return WarpQuorum{}, fmt.Errorf("failed to find warp config for blockchain %s", blockchainID)
return warpConfig, nil
}

func (c *Config) InitializeWarpQuorums() error {
// Initializes Warp configurations (quorum and self-signing settings) for each destination subnet
func (c *Config) InitializeWarpConfigs() error {
// Fetch the Warp quorum values for each destination subnet.
for _, destinationSubnet := range c.DestinationBlockchains {
err := destinationSubnet.initializeWarpQuorum()
err := destinationSubnet.initializeWarpConfigs()
if err != nil {
return fmt.Errorf(
"failed to initialize Warp quorum for destination subnet %s: %w",
Expand Down Expand Up @@ -256,6 +243,15 @@ func (c *Config) GetWarpQuorum(blockchainID ids.ID) (WarpQuorum, error) {
return WarpQuorum{}, errFailedToGetWarpQuorum
}

func (c *Config) GetWarpRequirePrimaryNetworkSigners(blockchainID ids.ID) (bool, error) {
for _, s := range c.DestinationBlockchains {
if blockchainID.String() == s.BlockchainID {
return s.warpRequirePrimaryNetworkSigners, nil
}
}
iansuvak marked this conversation as resolved.
Show resolved Hide resolved
return false, errFailedToGetWarpQuorum
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this error should say anything about the Warp Quorum. We error here iff the destination blockchain is in the config.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the wording to indicate that the blockchainID is not in the destination blockchains. Thanks!

}

var _ peers.Config = &Config{}

func (c *Config) GetPChainAPI() *basecfg.APIConfig {
Expand Down
80 changes: 60 additions & 20 deletions relayer/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,32 +242,22 @@ func TestEitherKMSOrAccountPrivateKey(t *testing.T) {
}
}

func TestGetWarpQuorum(t *testing.T) {
func TestGetWarpConfig(t *testing.T) {
blockchainID, err := ids.FromString("p433wpuXyJiDhyazPYyZMJeaoPSW76CBZ2x7wrVPLgvokotXz")
require.NoError(t, err)
subnetID, err := ids.FromString("2PsShLjrFFwR51DMcAh8pyuwzLn1Ym3zRhuXLTmLCR1STk2mL6")
require.NoError(t, err)

testCases := []struct {
name string
blockchainID ids.ID
subnetID ids.ID
chainConfig params.ChainConfigWithUpgradesJSON
getChainConfigCalls int
expectedError error
expectedQuorum WarpQuorum
name string
blockchainID ids.ID
subnetID ids.ID
chainConfig params.ChainConfigWithUpgradesJSON
getChainConfigCalls int
expectedError error
expectedQuorum WarpQuorum
expectedRequirePrimaryNeworkSigners bool
}{
{
name: "primary network",
blockchainID: blockchainID,
subnetID: ids.Empty,
getChainConfigCalls: 0,
expectedError: nil,
expectedQuorum: WarpQuorum{
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
},
},
{
name: "subnet genesis precompile",
blockchainID: blockchainID,
Expand All @@ -287,6 +277,7 @@ func TestGetWarpQuorum(t *testing.T) {
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
},
expectedRequirePrimaryNeworkSigners: false,
},
{
name: "subnet genesis precompile non-default",
Expand All @@ -307,6 +298,7 @@ func TestGetWarpQuorum(t *testing.T) {
QuorumNumerator: 50,
QuorumDenominator: warp.WarpQuorumDenominator,
},
expectedRequirePrimaryNeworkSigners: false,
},
{
name: "subnet upgrade precompile",
Expand All @@ -329,6 +321,7 @@ func TestGetWarpQuorum(t *testing.T) {
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
},
expectedRequirePrimaryNeworkSigners: false,
},
{
name: "subnet upgrade precompile non-default",
Expand All @@ -351,6 +344,51 @@ func TestGetWarpQuorum(t *testing.T) {
QuorumNumerator: 50,
QuorumDenominator: warp.WarpQuorumDenominator,
},
expectedRequirePrimaryNeworkSigners: false,
},
{
name: "require primary network signers",
blockchainID: blockchainID,
subnetID: subnetID,
getChainConfigCalls: 1,
chainConfig: params.ChainConfigWithUpgradesJSON{
ChainConfig: params.ChainConfig{
GenesisPrecompiles: params.Precompiles{
warpConfigKey: &warp.Config{
QuorumNumerator: 0,
RequirePrimaryNetworkSigners: true,
},
},
},
},
expectedError: nil,
expectedQuorum: WarpQuorum{
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
},
expectedRequirePrimaryNeworkSigners: true,
},
{
name: "require primary network signers explicit false",
blockchainID: blockchainID,
subnetID: subnetID,
getChainConfigCalls: 1,
chainConfig: params.ChainConfigWithUpgradesJSON{
ChainConfig: params.ChainConfig{
GenesisPrecompiles: params.Precompiles{
warpConfigKey: &warp.Config{
QuorumNumerator: 0,
RequirePrimaryNetworkSigners: false,
},
},
},
},
expectedError: nil,
expectedQuorum: WarpQuorum{
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
},
expectedRequirePrimaryNeworkSigners: false,
},
}

Expand All @@ -364,9 +402,11 @@ func TestGetWarpQuorum(t *testing.T) {
).Times(testCase.getChainConfigCalls),
)

quorum, err := getWarpQuorum(testCase.subnetID, testCase.blockchainID, client)
warpConfig, err := getWarpConfig(client)
require.Equal(t, testCase.expectedError, err)
quorum := calculateQuorum(warpConfig.QuorumNumerator)
require.Equal(t, testCase.expectedQuorum, quorum)
require.Equal(t, testCase.expectedRequirePrimaryNeworkSigners, warpConfig.RequirePrimaryNetworkSigners)
})
}
}
Expand Down
26 changes: 19 additions & 7 deletions relayer/config/destination_blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (
"fmt"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/constants"
basecfg "github.com/ava-labs/awm-relayer/config"
"github.com/ava-labs/awm-relayer/utils"
"github.com/ava-labs/subnet-evm/precompile/contracts/warp"
"github.com/ethereum/go-ethereum/crypto"
)

Expand All @@ -23,7 +25,8 @@ type DestinationBlockchain struct {
AccountPrivateKey string `mapstructure:"account-private-key" json:"account-private-key"`

// Fetched from the chain after startup
warpQuorum WarpQuorum
warpQuorum WarpQuorum
warpRequirePrimaryNetworkSigners bool

// convenience fields to access parsed data after initialization
subnetID ids.ID
Expand Down Expand Up @@ -77,7 +80,7 @@ func (s *DestinationBlockchain) GetBlockchainID() ids.ID {
return s.blockchainID
}

func (s *DestinationBlockchain) initializeWarpQuorum() error {
func (s *DestinationBlockchain) initializeWarpConfigs() error {
blockchainID, err := ids.FromString(s.BlockchainID)
if err != nil {
return fmt.Errorf("invalid blockchainID in configuration. error: %w", err)
Expand All @@ -86,23 +89,32 @@ func (s *DestinationBlockchain) initializeWarpQuorum() error {
if err != nil {
return fmt.Errorf("invalid subnetID in configuration. error: %w", err)
}
// If the destination blockchain is the primary network, use the default quorum
// primary network signers here are irrelevant and can be left at default value
if subnetID == constants.PrimaryNetworkID {
s.warpQuorum = WarpQuorum{
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
}
return nil
}

client, err := utils.NewEthClientWithConfig(
context.Background(),
s.RPCEndpoint.BaseURL,
s.RPCEndpoint.HTTPHeaders,
s.RPCEndpoint.QueryParams,
)
defer client.Close()
if err != nil {
return fmt.Errorf("failed to dial destination blockchain %s: %w", blockchainID, err)
}
defer client.Close()
quorum, err := getWarpQuorum(subnetID, blockchainID, client)
warpConfig, err := getWarpConfig(client)
if err != nil {
return fmt.Errorf("failed to fetch warp quorum for subnet %s: %w", subnetID, err)
return fmt.Errorf("failed to fetch warp config for blockchain %s: %w", blockchainID, err)
}

s.warpQuorum = quorum
s.warpQuorum = calculateQuorum(warpConfig.QuorumNumerator)
s.warpRequirePrimaryNetworkSigners = warpConfig.RequirePrimaryNetworkSigners
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion relayer/main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func main() {
}
// Initialize the Warp Quorum values by fetching via RPC
// We do this here so that BuildConfig doesn't need to make RPC calls
if err = cfg.InitializeWarpQuorums(); err != nil {
if err = cfg.InitializeWarpConfigs(); err != nil {
panic(fmt.Errorf("couldn't initialize warp quorums: %w", err))
}

Expand Down