github.com/MetalBlockchain/metalgo@v1.11.9/subnets/subnet.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package subnets
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/MetalBlockchain/metalgo/ids"
    10  	"github.com/MetalBlockchain/metalgo/snow/engine/common"
    11  	"github.com/MetalBlockchain/metalgo/utils/set"
    12  )
    13  
    14  var _ Subnet = (*subnet)(nil)
    15  
    16  type Allower interface {
    17  	// IsAllowed filters out nodes that are not allowed to connect to this subnet
    18  	IsAllowed(nodeID ids.NodeID, isValidator bool) bool
    19  }
    20  
    21  // Subnet keeps track of the currently bootstrapping chains in a subnet. If no
    22  // chains in the subnet are currently bootstrapping, the subnet is considered
    23  // bootstrapped.
    24  type Subnet interface {
    25  	common.BootstrapTracker
    26  
    27  	// AddChain adds a chain to this Subnet
    28  	AddChain(chainID ids.ID) bool
    29  
    30  	// Config returns config of this Subnet
    31  	Config() Config
    32  
    33  	Allower
    34  }
    35  
    36  type subnet struct {
    37  	lock             sync.RWMutex
    38  	bootstrapping    set.Set[ids.ID]
    39  	bootstrapped     set.Set[ids.ID]
    40  	once             sync.Once
    41  	bootstrappedSema chan struct{}
    42  	config           Config
    43  	myNodeID         ids.NodeID
    44  }
    45  
    46  func New(myNodeID ids.NodeID, config Config) Subnet {
    47  	return &subnet{
    48  		bootstrappedSema: make(chan struct{}),
    49  		config:           config,
    50  		myNodeID:         myNodeID,
    51  	}
    52  }
    53  
    54  func (s *subnet) IsBootstrapped() bool {
    55  	s.lock.RLock()
    56  	defer s.lock.RUnlock()
    57  
    58  	return s.bootstrapping.Len() == 0
    59  }
    60  
    61  func (s *subnet) Bootstrapped(chainID ids.ID) {
    62  	s.lock.Lock()
    63  	defer s.lock.Unlock()
    64  
    65  	s.bootstrapping.Remove(chainID)
    66  	s.bootstrapped.Add(chainID)
    67  	if s.bootstrapping.Len() > 0 {
    68  		return
    69  	}
    70  
    71  	s.once.Do(func() {
    72  		close(s.bootstrappedSema)
    73  	})
    74  }
    75  
    76  func (s *subnet) OnBootstrapCompleted() chan struct{} {
    77  	return s.bootstrappedSema
    78  }
    79  
    80  func (s *subnet) AddChain(chainID ids.ID) bool {
    81  	s.lock.Lock()
    82  	defer s.lock.Unlock()
    83  
    84  	if s.bootstrapping.Contains(chainID) || s.bootstrapped.Contains(chainID) {
    85  		return false
    86  	}
    87  
    88  	s.bootstrapping.Add(chainID)
    89  	return true
    90  }
    91  
    92  func (s *subnet) Config() Config {
    93  	return s.config
    94  }
    95  
    96  func (s *subnet) IsAllowed(nodeID ids.NodeID, isValidator bool) bool {
    97  	// Case 1: NodeID is this node
    98  	// Case 2: This subnet is not validator-only subnet
    99  	// Case 3: NodeID is a validator for this chain
   100  	// Case 4: NodeID is explicitly allowed whether it's subnet validator or not
   101  	return nodeID == s.myNodeID ||
   102  		!s.config.ValidatorOnly ||
   103  		isValidator ||
   104  		s.config.AllowedNodes.Contains(nodeID)
   105  }