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

Fetch warp numerator (rebased off of off-chain-warp-messages) #157

Merged
merged 31 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a5778bb
remove panics
cam-schultz Jan 3, 2024
457ff9d
rename func
cam-schultz Jan 3, 2024
d7fdec2
fetch chainconfig from subnet-evm
cam-schultz Jan 3, 2024
64b35f3
hardcode primary network quorum
cam-schultz Jan 3, 2024
b232ab6
use fetched quorum when relaying
cam-schultz Jan 3, 2024
94fd1de
check upgrade precompile
cam-schultz Jan 4, 2024
be2248c
set numerator helper
cam-schultz Jan 4, 2024
8fcd55d
get quorum unit test
cam-schultz Jan 4, 2024
285ddc4
review feedback
cam-schultz Jan 5, 2024
aefe57b
use destination warp quorum to gather signatures
cam-schultz Jan 8, 2024
6c454b5
lint
cam-schultz Jan 8, 2024
9caa414
use most recent upgrade config
cam-schultz Jan 9, 2024
d9fc2f3
use upgrades config directly
cam-schultz Jan 9, 2024
16c9bd2
comment
cam-schultz Jan 9, 2024
60c3ad5
fix typo
cam-schultz Jan 10, 2024
d931ab9
decouple build cfg and init warp quorum
cam-schultz Jan 26, 2024
294e68b
use destination warp quorum
cam-schultz Jan 26, 2024
1398c95
compase relayer with cfg
cam-schultz Feb 2, 2024
825abc1
panic on error in main
cam-schultz Feb 2, 2024
fe1072c
rename helper
cam-schultz Feb 2, 2024
fb21e04
reorder args
cam-schultz Feb 2, 2024
c08dd3f
simplify control flow
cam-schultz Feb 2, 2024
8fe9319
warpConfig const
cam-schultz Feb 2, 2024
a420845
remove source subnet warp quorum init
cam-schultz Feb 2, 2024
c77b20d
add warp quorum to destinationsubnet
cam-schultz Feb 2, 2024
b2b6bec
message relayer metrics error checking
cam-schultz Feb 2, 2024
1d50965
unmarshal cfg into pointer slices
cam-schultz Feb 2, 2024
4cf4da7
consistent errors
cam-schultz Feb 2, 2024
e7a6e7a
Merge branch 'off-chain-warp-messages' into fetch-warp-numerator-rebase
cam-schultz Feb 5, 2024
eb4f790
Merge branch 'off-chain-warp-messages' into fetch-warp-numerator-rebase
geoff-vball Feb 6, 2024
d5390d4
Merge branch 'main' into fetch-warp-numerator-rebase
cam-schultz Feb 7, 2024
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
122 changes: 122 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package config

import (
"context"
"crypto/ecdsa"
"encoding/hex"
"fmt"
Expand All @@ -15,6 +16,8 @@ import (
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/ava-labs/awm-relayer/utils"
"github.com/ava-labs/subnet-evm/ethclient"
"github.com/ava-labs/subnet-evm/precompile/contracts/warp"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/spf13/viper"
Expand All @@ -24,6 +27,7 @@ const (
relayerPrivateKeyBytes = 32
accountPrivateKeyEnvVarName = "ACCOUNT_PRIVATE_KEY"
cChainIdentifierString = "C"
warpConfigKey = "warpConfig"
)

type MessageProtocolConfig struct {
Expand Down Expand Up @@ -72,6 +76,14 @@ type DestinationSubnet struct {
EncryptConnection bool `mapstructure:"encrypt-connection" json:"encrypt-connection"`
RPCEndpoint string `mapstructure:"rpc-endpoint" json:"rpc-endpoint"`
AccountPrivateKey string `mapstructure:"account-private-key" json:"account-private-key"`

// Fetched from the chain after startup
warpQuorum WarpQuorum
}

type WarpQuorum struct {
QuorumNumerator uint64
QuorumDenominator uint64
}

type Config struct {
Expand Down Expand Up @@ -294,6 +306,83 @@ func (m *ManualWarpMessage) Validate() error {
return nil
}

// If the numerator in the Warp config is 0, use the default value
func calculateQuorumNumerator(cfgNumerator uint64) uint64 {
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
}

// 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: %v", blockchainID, err)
}

// First, check the list of precompile upgrades to get the most up to date Warp config
// We only need to consider the most recent Warp config, since the QuorumNumerator is used
// at signature verification time on the receiving chain, regardless of the Warp config at the
// time of the message's creation
var warpConfig *warp.Config
for _, precompile := range chainConfig.UpgradeConfig.PrecompileUpgrades {
cfg, ok := precompile.Config.(*warp.Config)
if !ok {
continue
}
if warpConfig == nil {
warpConfig = cfg
continue
}
if *cfg.Timestamp() > *warpConfig.Timestamp() {
warpConfig = cfg
}
}
if warpConfig != nil {
return WarpQuorum{
QuorumNumerator: calculateQuorumNumerator(warpConfig.QuorumNumerator),
QuorumDenominator: warp.WarpQuorumDenominator,
}, 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
}
return WarpQuorum{}, fmt.Errorf("failed to find warp config for blockchain %s", blockchainID)
}

func (c *Config) InitializeWarpQuorums() error {
// Fetch the Warp quorum values for each destination subnet.
for i, destinationSubnet := range c.DestinationSubnets {
err := destinationSubnet.initializeWarpQuorum()
if err != nil {
return fmt.Errorf("failed to initialize Warp quorum for destination subnet %s: %v", destinationSubnet.SubnetID, err)
}
c.DestinationSubnets[i] = destinationSubnet
}

return nil
}

func (s *SourceSubnet) GetSupportedDestinations() set.Set[ids.ID] {
return s.supportedDestinations
}
Expand Down Expand Up @@ -379,6 +468,30 @@ func (s *DestinationSubnet) Validate() error {
return nil
}

func (s *DestinationSubnet) initializeWarpQuorum() error {
blockchainID, err := ids.FromString(s.BlockchainID)
if err != nil {
return fmt.Errorf("invalid blockchainID in configuration. error: %v", err)
}
subnetID, err := ids.FromString(s.SubnetID)
if err != nil {
return fmt.Errorf("invalid subnetID in configuration. error: %v", err)
}

client, err := ethclient.Dial(s.GetNodeRPCEndpoint())
if err != nil {
return fmt.Errorf("failed to dial destination blockchain %s: %v", blockchainID, err)
}
defer client.Close()
quorum, err := getWarpQuorum(subnetID, blockchainID, client)
if err != nil {
return err
}

s.warpQuorum = quorum
return nil
}

func constructURL(protocol string, host string, port uint32, encrypt bool, blockchainIDStr string, subnetIDStr string) string {
var protocolPathMap = map[string]string{
"http": "rpc",
Expand Down Expand Up @@ -481,3 +594,12 @@ func (s *DestinationSubnet) GetRelayerAccountInfo() (*ecdsa.PrivateKey, common.A
func (c *Config) GetSourceIDs() ([]ids.ID, []ids.ID) {
return c.sourceSubnetIDs, c.sourceBlockchainIDs
}

func (c *Config) GetWarpQuorum(blockchainID ids.ID) (WarpQuorum, bool) {
for _, s := range c.DestinationSubnets {
if blockchainID.String() == s.BlockchainID {
return s.warpQuorum, true
}
}
return WarpQuorum{}, false
}
131 changes: 131 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ import (
"path/filepath"
"testing"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/awm-relayer/utils"
mock_ethclient "github.com/ava-labs/awm-relayer/vms/evm/mocks"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/precompile/contracts/warp"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)

var (
Expand Down Expand Up @@ -411,3 +416,129 @@ func TestGetRelayerAccountInfoSkipChainConfigCheckCompatible(t *testing.T) {
require.NoError(t, err)
require.Equal(t, expectedAddress, address.String())
}

func TestGetWarpQuorum(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: "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,
subnetID: subnetID,
getChainConfigCalls: 1,
chainConfig: params.ChainConfigWithUpgradesJSON{
ChainConfig: params.ChainConfig{
GenesisPrecompiles: params.Precompiles{
warpConfigKey: &warp.Config{
QuorumNumerator: 0,
},
},
},
},
expectedError: nil,
expectedQuorum: WarpQuorum{
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
},
},
{
name: "subnet genesis precompile non-default",
blockchainID: blockchainID,
subnetID: subnetID,
getChainConfigCalls: 1,
chainConfig: params.ChainConfigWithUpgradesJSON{
ChainConfig: params.ChainConfig{
GenesisPrecompiles: params.Precompiles{
warpConfigKey: &warp.Config{
QuorumNumerator: 50,
},
},
},
},
expectedError: nil,
expectedQuorum: WarpQuorum{
QuorumNumerator: 50,
QuorumDenominator: warp.WarpQuorumDenominator,
},
},
{
name: "subnet upgrade precompile",
blockchainID: blockchainID,
subnetID: subnetID,
getChainConfigCalls: 1,
chainConfig: params.ChainConfigWithUpgradesJSON{
UpgradeConfig: params.UpgradeConfig{
PrecompileUpgrades: []params.PrecompileUpgrade{
{
Config: &warp.Config{
QuorumNumerator: 0,
},
},
},
},
},
expectedError: nil,
expectedQuorum: WarpQuorum{
QuorumNumerator: warp.WarpDefaultQuorumNumerator,
QuorumDenominator: warp.WarpQuorumDenominator,
},
},
{
name: "subnet upgrade precompile non-default",
blockchainID: blockchainID,
subnetID: subnetID,
getChainConfigCalls: 1,
chainConfig: params.ChainConfigWithUpgradesJSON{
UpgradeConfig: params.UpgradeConfig{
PrecompileUpgrades: []params.PrecompileUpgrade{
{
Config: &warp.Config{
QuorumNumerator: 50,
},
},
},
},
},
expectedError: nil,
expectedQuorum: WarpQuorum{
QuorumNumerator: 50,
QuorumDenominator: warp.WarpQuorumDenominator,
},
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
client := mock_ethclient.NewMockClient(gomock.NewController(t))
gomock.InOrder(
client.EXPECT().ChainConfig(gomock.Any()).Return(&testCase.chainConfig, nil).Times(testCase.getChainConfigCalls),
)

quorum, err := getWarpQuorum(testCase.subnetID, testCase.blockchainID, client)
require.Equal(t, testCase.expectedError, err)
require.Equal(t, testCase.expectedQuorum, quorum)
})
}
}
Loading
Loading