Skip to content

Commit

Permalink
Merge branch 'master' into update-versions-v1.11.0-fuji
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenButtolph authored Feb 19, 2024
2 parents 5ccd9c3 + d372402 commit e0ed581
Show file tree
Hide file tree
Showing 60 changed files with 2,463 additions and 744 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
os: [macos-12, ubuntu-20.04, ubuntu-22.04, windows-2022, [self-hosted, linux, ARM64, focal], [self-hosted, linux, ARM64, jammy]]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/setup-go@v3
with:
go-version: ${{ env.go_version }}
check-latest: true
Expand Down
63 changes: 9 additions & 54 deletions chains/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ var (
errUnknownVMType = errors.New("the vm should have type avalanche.DAGVM or snowman.ChainVM")
errCreatePlatformVM = errors.New("attempted to create a chain running the PlatformVM")
errNotBootstrapped = errors.New("subnets not bootstrapped")
errNoPrimaryNetworkConfig = errors.New("no subnet config for primary network found")
errPartialSyncAsAValidator = errors.New("partial sync should not be configured for a validator")

fxs = map[ids.ID]fx.Factory{
Expand Down Expand Up @@ -230,6 +229,8 @@ type ManagerConfig struct {
StateSyncBeacons []ids.NodeID

ChainDataDir string

Subnets *Subnets
}

type manager struct {
Expand All @@ -253,11 +254,6 @@ type manager struct {
chainCreatorShutdownCh chan struct{}
chainCreatorExited sync.WaitGroup

subnetsLock sync.RWMutex
// Key: Subnet's ID
// Value: Subnet description
subnets map[ids.ID]subnets.Subnet

chainsLock sync.Mutex
// Key: Chain's ID
// Value: The chain
Expand All @@ -274,7 +270,6 @@ func New(config *ManagerConfig) Manager {
ManagerConfig: *config,
stakingSigner: config.StakingTLSCert.PrivateKey.(crypto.Signer),
stakingCert: staking.CertificateFromX509(config.StakingTLSCert.Leaf),
subnets: make(map[ids.ID]subnets.Subnet),
chains: make(map[ids.ID]handler.Handler),
chainsQueue: buffer.NewUnboundedBlockingDeque[ChainParameters](initialQueueSize),
unblockChainCreatorCh: make(chan struct{}),
Expand All @@ -285,25 +280,10 @@ func New(config *ManagerConfig) Manager {
// QueueChainCreation queues a chain creation request
// Invariant: Tracked Subnet must be checked before calling this function
func (m *manager) QueueChainCreation(chainParams ChainParameters) {
m.subnetsLock.Lock()
subnetID := chainParams.SubnetID
sb, exists := m.subnets[subnetID]
if !exists {
sbConfig, ok := m.SubnetConfigs[subnetID]
if !ok {
// default to primary subnet config
sbConfig = m.SubnetConfigs[constants.PrimaryNetworkID]
}
sb = subnets.New(m.NodeID, sbConfig)
m.subnets[chainParams.SubnetID] = sb
}
addedChain := sb.AddChain(chainParams.ID)
m.subnetsLock.Unlock()

if !addedChain {
if sb, _ := m.Subnets.GetOrCreate(chainParams.SubnetID); !sb.AddChain(chainParams.ID) {
m.Log.Debug("skipping chain creation",
zap.String("reason", "chain already staged"),
zap.Stringer("subnetID", subnetID),
zap.Stringer("subnetID", chainParams.SubnetID),
zap.Stringer("chainID", chainParams.ID),
zap.Stringer("vmID", chainParams.VMID),
)
Expand All @@ -313,7 +293,7 @@ func (m *manager) QueueChainCreation(chainParams ChainParameters) {
if ok := m.chainsQueue.PushRight(chainParams); !ok {
m.Log.Warn("skipping chain creation",
zap.String("reason", "couldn't enqueue chain"),
zap.Stringer("subnetID", subnetID),
zap.Stringer("subnetID", chainParams.SubnetID),
zap.Stringer("chainID", chainParams.ID),
zap.Stringer("vmID", chainParams.VMID),
)
Expand All @@ -331,9 +311,7 @@ func (m *manager) createChain(chainParams ChainParameters) {
zap.Stringer("vmID", chainParams.VMID),
)

m.subnetsLock.RLock()
sb := m.subnets[chainParams.SubnetID]
m.subnetsLock.RUnlock()
sb, _ := m.Subnets.GetOrCreate(chainParams.SubnetID)

// Note: buildChain builds all chain's relevant objects (notably engine and handler)
// but does not start their operations. Starting of the handler (which could potentially
Expand Down Expand Up @@ -1304,23 +1282,9 @@ func (m *manager) IsBootstrapped(id ids.ID) bool {
return chain.Context().State.Get().State == snow.NormalOp
}

func (m *manager) subnetsNotBootstrapped() []ids.ID {
m.subnetsLock.RLock()
defer m.subnetsLock.RUnlock()

subnetsBootstrapping := make([]ids.ID, 0, len(m.subnets))
for subnetID, subnet := range m.subnets {
if !subnet.IsBootstrapped() {
subnetsBootstrapping = append(subnetsBootstrapping, subnetID)
}
}
return subnetsBootstrapping
}

func (m *manager) registerBootstrappedHealthChecks() error {
bootstrappedCheck := health.CheckerFunc(func(context.Context) (interface{}, error) {
subnetIDs := m.subnetsNotBootstrapped()
if len(subnetIDs) != 0 {
if subnetIDs := m.Subnets.Bootstrapping(); len(subnetIDs) != 0 {
return subnetIDs, errNotBootstrapped
}
return []ids.ID{}, nil
Expand Down Expand Up @@ -1362,18 +1326,9 @@ func (m *manager) registerBootstrappedHealthChecks() error {

// Starts chain creation loop to process queued chains
func (m *manager) StartChainCreator(platformParams ChainParameters) error {
// Get the Primary Network's subnet config. If it wasn't registered, then we
// throw a fatal error.
sbConfig, ok := m.SubnetConfigs[constants.PrimaryNetworkID]
if !ok {
return errNoPrimaryNetworkConfig
}

sb := subnets.New(m.NodeID, sbConfig)
m.subnetsLock.Lock()
m.subnets[platformParams.SubnetID] = sb
// Add the P-Chain to the Primary Network
sb, _ := m.Subnets.GetOrCreate(constants.PrimaryNetworkID)
sb.AddChain(platformParams.ID)
m.subnetsLock.Unlock()

// The P-chain is created synchronously to ensure that `VM.Initialize` has
// finished before returning from this function. This is required because
Expand Down
82 changes: 82 additions & 0 deletions chains/subnets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package chains

import (
"errors"
"sync"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/subnets"
"github.com/ava-labs/avalanchego/utils/constants"
)

var ErrNoPrimaryNetworkConfig = errors.New("no subnet config for primary network found")

// Subnets holds the currently running subnets on this node
type Subnets struct {
nodeID ids.NodeID
configs map[ids.ID]subnets.Config

lock sync.RWMutex
subnets map[ids.ID]subnets.Subnet
}

// GetOrCreate returns a subnet running on this node, or creates one if it was
// not running before. Returns the subnet and if the subnet was created.
func (s *Subnets) GetOrCreate(subnetID ids.ID) (subnets.Subnet, bool) {
s.lock.Lock()
defer s.lock.Unlock()

if subnet, ok := s.subnets[subnetID]; ok {
return subnet, false
}

// Default to the primary network config if a subnet config was not
// specified
config, ok := s.configs[subnetID]
if !ok {
config = s.configs[constants.PrimaryNetworkID]
}

subnet := subnets.New(s.nodeID, config)
s.subnets[subnetID] = subnet

return subnet, true
}

// Bootstrapping returns the subnetIDs of any chains that are still
// bootstrapping.
func (s *Subnets) Bootstrapping() []ids.ID {
s.lock.RLock()
defer s.lock.RUnlock()

subnetsBootstrapping := make([]ids.ID, 0, len(s.subnets))
for subnetID, subnet := range s.subnets {
if !subnet.IsBootstrapped() {
subnetsBootstrapping = append(subnetsBootstrapping, subnetID)
}
}

return subnetsBootstrapping
}

// NewSubnets returns an instance of Subnets
func NewSubnets(
nodeID ids.NodeID,
configs map[ids.ID]subnets.Config,
) (*Subnets, error) {
if _, ok := configs[constants.PrimaryNetworkID]; !ok {
return nil, ErrNoPrimaryNetworkConfig
}

s := &Subnets{
nodeID: nodeID,
configs: configs,
subnets: make(map[ids.ID]subnets.Subnet),
}

_, _ = s.GetOrCreate(constants.PrimaryNetworkID)
return s, nil
}
173 changes: 173 additions & 0 deletions chains/subnets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package chains

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/subnets"
"github.com/ava-labs/avalanchego/utils/constants"
)

func TestNewSubnets(t *testing.T) {
require := require.New(t)
config := map[ids.ID]subnets.Config{
constants.PrimaryNetworkID: {},
}

subnets, err := NewSubnets(ids.EmptyNodeID, config)
require.NoError(err)

subnet, ok := subnets.GetOrCreate(constants.PrimaryNetworkID)
require.False(ok)
require.Equal(config[constants.PrimaryNetworkID], subnet.Config())
}

func TestNewSubnetsNoPrimaryNetworkConfig(t *testing.T) {
require := require.New(t)
config := map[ids.ID]subnets.Config{}

_, err := NewSubnets(ids.EmptyNodeID, config)
require.ErrorIs(err, ErrNoPrimaryNetworkConfig)
}

func TestSubnetsGetOrCreate(t *testing.T) {
testSubnetID := ids.GenerateTestID()

type args struct {
subnetID ids.ID
want bool
}

tests := []struct {
name string
args []args
}{
{
name: "adding duplicate subnet is a noop",
args: []args{
{
subnetID: testSubnetID,
want: true,
},
{
subnetID: testSubnetID,
},
},
},
{
name: "adding unique subnets succeeds",
args: []args{
{
subnetID: ids.GenerateTestID(),
want: true,
},
{
subnetID: ids.GenerateTestID(),
want: true,
},
{
subnetID: ids.GenerateTestID(),
want: true,
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require := require.New(t)
config := map[ids.ID]subnets.Config{
constants.PrimaryNetworkID: {},
}
subnets, err := NewSubnets(ids.EmptyNodeID, config)
require.NoError(err)

for _, arg := range tt.args {
_, got := subnets.GetOrCreate(arg.subnetID)
require.Equal(arg.want, got)
}
})
}
}

func TestSubnetConfigs(t *testing.T) {
testSubnetID := ids.GenerateTestID()

tests := []struct {
name string
config map[ids.ID]subnets.Config
subnetID ids.ID
want subnets.Config
}{
{
name: "default to primary network config",
config: map[ids.ID]subnets.Config{
constants.PrimaryNetworkID: {},
},
subnetID: testSubnetID,
want: subnets.Config{},
},
{
name: "use subnet config",
config: map[ids.ID]subnets.Config{
constants.PrimaryNetworkID: {},
testSubnetID: {
GossipConfig: subnets.GossipConfig{
AcceptedFrontierValidatorSize: 123456789,
},
},
},
subnetID: testSubnetID,
want: subnets.Config{
GossipConfig: subnets.GossipConfig{
AcceptedFrontierValidatorSize: 123456789,
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require := require.New(t)

subnets, err := NewSubnets(ids.EmptyNodeID, tt.config)
require.NoError(err)

subnet, ok := subnets.GetOrCreate(tt.subnetID)
require.True(ok)

require.Equal(tt.want, subnet.Config())
})
}
}

func TestSubnetsBootstrapping(t *testing.T) {
require := require.New(t)

config := map[ids.ID]subnets.Config{
constants.PrimaryNetworkID: {},
}

subnets, err := NewSubnets(ids.EmptyNodeID, config)
require.NoError(err)

subnetID := ids.GenerateTestID()
chainID := ids.GenerateTestID()

subnet, ok := subnets.GetOrCreate(subnetID)
require.True(ok)

// Start bootstrapping
subnet.AddChain(chainID)
bootstrapping := subnets.Bootstrapping()
require.Contains(bootstrapping, subnetID)

// Finish bootstrapping
subnet.Bootstrapped(chainID)
require.Empty(subnets.Bootstrapping())
}
Loading

0 comments on commit e0ed581

Please sign in to comment.