Skip to content

Commit

Permalink
Configure min connected stake based on Alpha and K (ava-labs#1990)
Browse files Browse the repository at this point in the history
Co-authored-by: Dan Laine <[email protected]>
  • Loading branch information
ceyonur and Dan Laine authored Oct 12, 2022
1 parent 5176495 commit 6b7629b
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 83 deletions.
26 changes: 17 additions & 9 deletions chains/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,8 @@ func (m *manager) buildChain(chainParams ChainParameters, sb Subnet) (*chain, er
// before it's first access would cause a panic.
ctx.SetState(snow.Initializing)

if sbConfigs, ok := m.SubnetConfigs[chainParams.SubnetID]; ok {
if sbConfigs.ValidatorOnly {
if subnetConfig, ok := m.SubnetConfigs[chainParams.SubnetID]; ok {
if subnetConfig.ValidatorOnly {
ctx.SetValidatorOnly()
}
}
Expand Down Expand Up @@ -450,11 +450,13 @@ func (m *manager) buildChain(chainParams ChainParameters, sb Subnet) (*chain, er
}

consensusParams := m.ConsensusParams
if sbConfigs, ok := m.SubnetConfigs[chainParams.SubnetID]; ok && chainParams.SubnetID != constants.PrimaryNetworkID {
consensusParams = sbConfigs.ConsensusParameters
// short circuit it before reading from subnetConfigs
if chainParams.SubnetID != constants.PrimaryNetworkID {
if subnetConfig, ok := m.SubnetConfigs[chainParams.SubnetID]; ok {
consensusParams = subnetConfig.ConsensusParameters
}
}

// The validators of this blockchain
var vdrs validators.Set // Validators validating this blockchain
var ok bool
if m.StakingEnabled {
Expand Down Expand Up @@ -569,8 +571,11 @@ func (m *manager) createAvalancheChain(
msgChan := make(chan common.Message, defaultChannelSize)

gossipConfig := m.GossipConfig
if sbConfigs, ok := m.SubnetConfigs[ctx.SubnetID]; ok && ctx.SubnetID != constants.PrimaryNetworkID {
gossipConfig = sbConfigs.GossipConfig
// short circuit it before reading from subnetConfigs
if ctx.SubnetID != constants.PrimaryNetworkID {
if subnetConfig, ok := m.SubnetConfigs[ctx.SubnetID]; ok {
gossipConfig = subnetConfig.GossipConfig
}
}

// Passes messages from the consensus engine to the network
Expand Down Expand Up @@ -757,8 +762,11 @@ func (m *manager) createSnowmanChain(
msgChan := make(chan common.Message, defaultChannelSize)

gossipConfig := m.GossipConfig
if sbConfigs, ok := m.SubnetConfigs[ctx.SubnetID]; ok && ctx.SubnetID != constants.PrimaryNetworkID {
gossipConfig = sbConfigs.GossipConfig
// short circuit it before reading from subnetConfigs
if ctx.SubnetID != constants.PrimaryNetworkID {
if subnetConfig, ok := m.SubnetConfigs[ctx.SubnetID]; ok {
gossipConfig = subnetConfig.GossipConfig
}
}

// Passes messages from the consensus engine to the network
Expand Down
83 changes: 47 additions & 36 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1007,11 +1007,7 @@ func getChainConfigsFromDir(v *viper.Viper) (map[string]chains.ChainConfig, erro
return make(map[string]chains.ChainConfig), nil
}

chainConfigs, err := readChainConfigPath(chainConfigPath)
if err != nil {
return nil, fmt.Errorf("couldn't read chain configs: %w", err)
}
return chainConfigs, nil
return readChainConfigPath(chainConfigPath)
}

// getChainConfigs reads & puts chainConfigs to node config
Expand Down Expand Up @@ -1060,6 +1056,13 @@ func readChainConfigPath(chainConfigPath string) (map[string]chains.ChainConfig,
return chainConfigMap, nil
}

func getSubnetConfigs(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]chains.SubnetConfig, error) {
if v.IsSet(SubnetConfigContentKey) {
return getSubnetConfigsFromFlags(v, subnetIDs)
}
return getSubnetConfigsFromDir(v, subnetIDs)
}

func getSubnetConfigsFromFlags(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]chains.SubnetConfig, error) {
subnetConfigContentB64 := v.GetString(SubnetConfigContentKey)
subnetConfigContent, err := base64.StdEncoding.DecodeString(subnetConfigContentB64)
Expand All @@ -1076,11 +1079,8 @@ func getSubnetConfigsFromFlags(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]c
res := make(map[ids.ID]chains.SubnetConfig)
for _, subnetID := range subnetIDs {
if rawSubnetConfigBytes, ok := subnetConfigs[subnetID]; ok {
subnetConfig := defaultSubnetConfig(v)
if err := json.Unmarshal(rawSubnetConfigBytes, &subnetConfig); err != nil {
return nil, err
}
if err := subnetConfig.ConsensusParameters.Valid(); err != nil {
subnetConfig, err := parseSubnetConfigs(rawSubnetConfigBytes, getDefaultSubnetConfig(v))
if err != nil {
return nil, err
}
res[subnetID] = subnetConfig
Expand All @@ -1100,22 +1100,7 @@ func getSubnetConfigsFromDir(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]cha
return make(map[ids.ID]chains.SubnetConfig), nil
}

subnetConfigs, err := readSubnetConfigs(subnetConfigPath, subnetIDs, defaultSubnetConfig(v))
if err != nil {
return nil, fmt.Errorf("couldn't read subnet configs: %w", err)
}
return subnetConfigs, nil
}

func getSubnetConfigs(v *viper.Viper, subnetIDs []ids.ID) (map[ids.ID]chains.SubnetConfig, error) {
if v.IsSet(SubnetConfigContentKey) {
return getSubnetConfigsFromFlags(v, subnetIDs)
}
return getSubnetConfigsFromDir(v, subnetIDs)
}

// readSubnetConfigs reads subnet config files from a path and given subnetIDs and returns a map.
func readSubnetConfigs(subnetConfigPath string, subnetIDs []ids.ID, defaultSubnetConfig chains.SubnetConfig) (map[ids.ID]chains.SubnetConfig, error) {
// reads subnet config files from a path and given subnetIDs and returns a map.
subnetConfigs := make(map[ids.ID]chains.SubnetConfig)
for _, subnetID := range subnetIDs {
filePath := filepath.Join(subnetConfigPath, subnetID.String()+subnetConfigFileExt)
Expand All @@ -1135,21 +1120,28 @@ func readSubnetConfigs(subnetConfigPath string, subnetIDs []ids.ID, defaultSubne
if err != nil {
return nil, err
}

configData := defaultSubnetConfig
if err := json.Unmarshal(file, &configData); err != nil {
return nil, err
}
if err := configData.ConsensusParameters.Valid(); err != nil {
config, err := parseSubnetConfigs(file, getDefaultSubnetConfig(v))
if err != nil {
return nil, err
}
subnetConfigs[subnetID] = configData
subnetConfigs[subnetID] = config
}

return subnetConfigs, nil
}

func defaultSubnetConfig(v *viper.Viper) chains.SubnetConfig {
func parseSubnetConfigs(data []byte, defaultSubnetConfig chains.SubnetConfig) (chains.SubnetConfig, error) {
if err := json.Unmarshal(data, &defaultSubnetConfig); err != nil {
return chains.SubnetConfig{}, err
}

if err := defaultSubnetConfig.ConsensusParameters.Valid(); err != nil {
return chains.SubnetConfig{}, fmt.Errorf("invalid consensus parameters: %w", err)
}
return defaultSubnetConfig, nil
}

func getDefaultSubnetConfig(v *viper.Viper) chains.SubnetConfig {
return chains.SubnetConfig{
ConsensusParameters: getConsensusConfig(v),
ValidatorOnly: false,
Expand Down Expand Up @@ -1341,14 +1333,24 @@ func GetNodeConfig(v *viper.Viper, buildDir string) (node.Config, error) {
// Subnet Configs
subnetConfigs, err := getSubnetConfigs(v, nodeConfig.WhitelistedSubnets.List())
if err != nil {
return node.Config{}, err
return node.Config{}, fmt.Errorf("couldn't read subnet configs: %w", err)
}
nodeConfig.SubnetConfigs = subnetConfigs

// Node health
nodeConfig.MinPercentConnectedStakeHealthy = map[ids.ID]float64{
constants.PrimaryNetworkID: calcMinConnectedStake(nodeConfig.ConsensusParams.Parameters),
}

nodeConfig.MinPercentConnectedStakeHealthy = make(map[ids.ID]float64)
for subnetID, config := range subnetConfigs {
nodeConfig.MinPercentConnectedStakeHealthy[subnetID] = calcMinConnectedStake(config.ConsensusParameters.Parameters)
}

// Chain Configs
nodeConfig.ChainConfigs, err = getChainConfigs(v)
if err != nil {
return node.Config{}, err
return node.Config{}, fmt.Errorf("couldn't read chain configs: %w", err)
}

// Profiler
Expand Down Expand Up @@ -1381,3 +1383,12 @@ func GetNodeConfig(v *viper.Viper, buildDir string) (node.Config, error) {
nodeConfig.DiskTargeterConfig, err = getDiskTargeterConfig(v)
return nodeConfig, err
}

// calcMinConnectedStake takes [consensusParams] as input and calculates the
// expected min connected stake percentage according to alpha and k.
func calcMinConnectedStake(consensusParams snowball.Parameters) float64 {
alpha := consensusParams.Alpha
k := consensusParams.K
r := float64(alpha) / float64(k)
return r*(1-constants.MinConnectedStakeBuffer) + constants.MinConnectedStakeBuffer
}
10 changes: 9 additions & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ func TestGetSubnetConfigsFromFile(t *testing.T) {
testF: func(require *require.Assertions, given map[ids.ID]chains.SubnetConfig) {
require.Nil(given)
},
errMessage: "couldn't read subnet configs",
errMessage: "invalid character",
},
"subnet is not whitelisted": {
fileName: "Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU.json",
Expand Down Expand Up @@ -578,6 +578,14 @@ func TestGetSubnetConfigsFromFlags(t *testing.T) {
}
}

func TestCalcMinConnectedStake(t *testing.T) {
v := setupViperFlags()
defaultParams := getConsensusConfig(v)
defaultExpectedMinStake := 0.8
minStake := calcMinConnectedStake(defaultParams.Parameters)
require.Equal(t, defaultExpectedMinStake, minStake)
}

// setups config json file and writes content
func setupConfigJSON(t *testing.T, rootPath string, value string) string {
configFilePath := filepath.Join(rootPath, "config.json")
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/ava-labs/avalanche-network-runner-sdk v0.2.0 h1:YNvM0oFlb7A825kGe0XwwZuvIXTKF1BsuvxJdRLhIaI=
github.com/ava-labs/avalanche-network-runner-sdk v0.2.0/go.mod h1:bEBRVZnGeRiNdDJAFUj+gA/TPzNDbpY/WzgDAHHwJb8=
github.com/ava-labs/coreth v0.11.1-rc.0 h1:NeVdLi2wTu8EjX5jmaVbdOmzHOl3PQCs6RoBcziMyXU=
github.com/ava-labs/coreth v0.11.1-rc.0/go.mod h1:jxBQyF3o5zMKJV1cnauJOcnPcvSEanvDXKUmcxlRAKE=
github.com/ava-labs/coreth v0.11.1-rc.1 h1:GpSythfFCOXBoalsNvmzFBae0rgblTUnQuamgAVEhu4=
github.com/ava-labs/coreth v0.11.1-rc.1/go.mod h1:8pN1Ko0RfPoEHzBVn9bzuv0uV1b+/vNTuYC+MqY8P0g=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
Expand Down
3 changes: 3 additions & 0 deletions node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,7 @@ type Config struct {

RequiredAvailableDiskSpace uint64 `json:"requiredAvailableDiskSpace"`
WarningThresholdAvailableDiskSpace uint64 `json:"warningThresholdAvailableDiskSpace"`

// See comment on [MinPercentConnectedStakeHealthy] in platformvm.Config
MinPercentConnectedStakeHealthy map[ids.ID]float64 `json:"minPercentConnectedStakeHealthy"`
}
53 changes: 27 additions & 26 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,32 +773,33 @@ func (n *Node) initVMs() error {
errs.Add(
vmRegisterer.Register(constants.PlatformVMID, &platformvm.Factory{
Config: config.Config{
Chains: n.chainManager,
Validators: vdrs,
SubnetTracker: n.Net,
UptimeLockedCalculator: n.uptimeCalculator,
StakingEnabled: n.Config.EnableStaking,
WhitelistedSubnets: n.Config.WhitelistedSubnets,
TxFee: n.Config.TxFee,
CreateAssetTxFee: n.Config.CreateAssetTxFee,
CreateSubnetTxFee: n.Config.CreateSubnetTxFee,
TransformSubnetTxFee: n.Config.TransformSubnetTxFee,
CreateBlockchainTxFee: n.Config.CreateBlockchainTxFee,
AddPrimaryNetworkValidatorFee: n.Config.AddPrimaryNetworkValidatorFee,
AddPrimaryNetworkDelegatorFee: n.Config.AddPrimaryNetworkDelegatorFee,
AddSubnetValidatorFee: n.Config.AddSubnetValidatorFee,
AddSubnetDelegatorFee: n.Config.AddSubnetDelegatorFee,
UptimePercentage: n.Config.UptimeRequirement,
MinValidatorStake: n.Config.MinValidatorStake,
MaxValidatorStake: n.Config.MaxValidatorStake,
MinDelegatorStake: n.Config.MinDelegatorStake,
MinDelegationFee: n.Config.MinDelegationFee,
MinStakeDuration: n.Config.MinStakeDuration,
MaxStakeDuration: n.Config.MaxStakeDuration,
RewardConfig: n.Config.RewardConfig,
ApricotPhase3Time: version.GetApricotPhase3Time(n.Config.NetworkID),
ApricotPhase5Time: version.GetApricotPhase5Time(n.Config.NetworkID),
BanffTime: version.GetBanffTime(n.Config.NetworkID),
Chains: n.chainManager,
Validators: vdrs,
SubnetTracker: n.Net,
UptimeLockedCalculator: n.uptimeCalculator,
StakingEnabled: n.Config.EnableStaking,
WhitelistedSubnets: n.Config.WhitelistedSubnets,
TxFee: n.Config.TxFee,
CreateAssetTxFee: n.Config.CreateAssetTxFee,
CreateSubnetTxFee: n.Config.CreateSubnetTxFee,
TransformSubnetTxFee: n.Config.TransformSubnetTxFee,
CreateBlockchainTxFee: n.Config.CreateBlockchainTxFee,
AddPrimaryNetworkValidatorFee: n.Config.AddPrimaryNetworkValidatorFee,
AddPrimaryNetworkDelegatorFee: n.Config.AddPrimaryNetworkDelegatorFee,
AddSubnetValidatorFee: n.Config.AddSubnetValidatorFee,
AddSubnetDelegatorFee: n.Config.AddSubnetDelegatorFee,
UptimePercentage: n.Config.UptimeRequirement,
MinValidatorStake: n.Config.MinValidatorStake,
MaxValidatorStake: n.Config.MaxValidatorStake,
MinDelegatorStake: n.Config.MinDelegatorStake,
MinDelegationFee: n.Config.MinDelegationFee,
MinStakeDuration: n.Config.MinStakeDuration,
MaxStakeDuration: n.Config.MaxStakeDuration,
RewardConfig: n.Config.RewardConfig,
ApricotPhase3Time: version.GetApricotPhase3Time(n.Config.NetworkID),
ApricotPhase5Time: version.GetApricotPhase5Time(n.Config.NetworkID),
BanffTime: version.GetBanffTime(n.Config.NetworkID),
MinPercentConnectedStakeHealthy: n.Config.MinPercentConnectedStakeHealthy,
},
}),
vmRegisterer.Register(constants.AVMID, &avm.Factory{
Expand Down
5 changes: 5 additions & 0 deletions utils/constants/networking.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ const (
DefaultByteSliceCap = 128

MaxContainersLen = int(4 * DefaultMaxMessageSize / 5)

// MinConnectedStakeBuffer is the safety buffer for calculation of MinConnectedStake.
// This increases the required stake percentage above alpha/k. Must be [0-1]
// 0 means MinConnectedStake = alpha/k, 1 means MinConnectedStake = 1 (fully connected)
MinConnectedStakeBuffer = .2
)
9 changes: 9 additions & 0 deletions vms/platformvm/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ type Config struct {

// Time of the Banff network upgrade
BanffTime time.Time

// Subnet ID --> Minimum portion of the subnet's stake this node must be
// connected to in order to report healthy.
// [constants.PrimaryNetworkID] is always a key in this map.
// If a subnet is in this map, but it isn't whitelisted, its corresponding
// value isn't used.
// If a subnet is whitelisted but not in this map, we use the value for the
// Primary Network.
MinPercentConnectedStakeHealthy map[ids.ID]float64
}

func (c *Config) IsApricotPhase3Activated(timestamp time.Time) bool {
Expand Down
Loading

0 comments on commit 6b7629b

Please sign in to comment.