@@ -115,6 +115,14 @@ type UptimeResult struct {
115
115
WeightedAveragePercentage float64
116
116
}
117
117
118
+ // To avoid potential deadlocks, we maintain that locks must be grabbed in the
119
+ // following order:
120
+ //
121
+ // 1. peersLock
122
+ // 2. manuallyTrackedIDsLock
123
+ //
124
+ // If a higher lock (e.g. manuallyTrackedIDsLock) is held when trying to grab a
125
+ // lower lock (e.g. peersLock) a deadlock could occur.
118
126
type network struct {
119
127
config * Config
120
128
peerConfig * peer.Config
@@ -154,11 +162,14 @@ type network struct {
154
162
// connect to. An entry is added to this set when we first start attempting
155
163
// to connect to the peer. An entry is deleted from this set once we have
156
164
// finished the handshake.
157
- trackedIPs map [ids.NodeID ]* trackedIP
158
- manuallyTrackedIDs set.Set [ids.NodeID ]
159
- connectingPeers peer.Set
160
- connectedPeers peer.Set
161
- closing bool
165
+ trackedIPs map [ids.NodeID ]* trackedIP
166
+ connectingPeers peer.Set
167
+ connectedPeers peer.Set
168
+ closing bool
169
+
170
+ // Tracks special peers that the network should always track
171
+ manuallyTrackedIDsLock sync.RWMutex
172
+ manuallyTrackedIDs set.Set [ids.NodeID ]
162
173
163
174
// router is notified about all peer [Connected] and [Disconnected] events
164
175
// as well as all non-handshake peer messages.
@@ -459,9 +470,7 @@ func (n *network) Connected(nodeID ids.NodeID) {
459
470
// of peers, then it should only connect if this node is a validator, or the
460
471
// peer is a validator/beacon.
461
472
func (n * network ) AllowConnection (nodeID ids.NodeID ) bool {
462
- return ! n .config .RequireValidatorToConnect ||
463
- n .config .Validators .Contains (constants .PrimaryNetworkID , n .config .MyNodeID ) ||
464
- n .WantsConnection (nodeID )
473
+ return ! n .config .RequireValidatorToConnect || n .WantsConnection (nodeID )
465
474
}
466
475
467
476
func (n * network ) Track (peerID ids.NodeID , claimedIPPorts []* ips.ClaimedIPPort ) ([]* p2p.PeerAck , error ) {
@@ -792,23 +801,24 @@ func (n *network) Dispatch() error {
792
801
}
793
802
794
803
func (n * network ) WantsConnection (nodeID ids.NodeID ) bool {
795
- n .peersLock .RLock ()
796
- defer n .peersLock .RUnlock ()
804
+ if n .config .Validators .Contains (constants .PrimaryNetworkID , nodeID ) {
805
+ return true
806
+ }
797
807
798
- return n . wantsConnection ( nodeID )
799
- }
808
+ n . manuallyTrackedIDsLock . RLock ( )
809
+ defer n . manuallyTrackedIDsLock . RUnlock ()
800
810
801
- func (n * network ) wantsConnection (nodeID ids.NodeID ) bool {
802
- return n .config .Validators .Contains (constants .PrimaryNetworkID , nodeID ) ||
803
- n .manuallyTrackedIDs .Contains (nodeID )
811
+ return n .manuallyTrackedIDs .Contains (nodeID )
804
812
}
805
813
806
814
func (n * network ) ManuallyTrack (nodeID ids.NodeID , ip ips.IPPort ) {
815
+ n .manuallyTrackedIDsLock .Lock ()
816
+ n .manuallyTrackedIDs .Add (nodeID )
817
+ n .manuallyTrackedIDsLock .Unlock ()
818
+
807
819
n .peersLock .Lock ()
808
820
defer n .peersLock .Unlock ()
809
821
810
- n .manuallyTrackedIDs .Add (nodeID )
811
-
812
822
_ , connected := n .connectedPeers .GetByID (nodeID )
813
823
if connected {
814
824
// If I'm currently connected to [nodeID] then they will have told me
@@ -949,7 +959,7 @@ func (n *network) disconnectedFromConnecting(nodeID ids.NodeID) {
949
959
// The peer that is disconnecting from us didn't finish the handshake
950
960
tracked , ok := n .trackedIPs [nodeID ]
951
961
if ok {
952
- if n .wantsConnection (nodeID ) {
962
+ if n .WantsConnection (nodeID ) {
953
963
tracked := tracked .trackNewIP (tracked .ip )
954
964
n .trackedIPs [nodeID ] = tracked
955
965
n .dial (nodeID , tracked )
@@ -972,7 +982,7 @@ func (n *network) disconnectedFromConnected(peer peer.Peer, nodeID ids.NodeID) {
972
982
n .connectedPeers .Remove (nodeID )
973
983
974
984
// The peer that is disconnecting from us finished the handshake
975
- if n .wantsConnection (nodeID ) {
985
+ if n .WantsConnection (nodeID ) {
976
986
prevIP := n .peerIPs [nodeID ]
977
987
tracked := newTrackedIP (prevIP .IPPort )
978
988
n .trackedIPs [nodeID ] = tracked
@@ -1028,7 +1038,7 @@ func (n *network) authenticateIPs(ips []*ips.ClaimedIPPort) ([]*ipAuth, error) {
1028
1038
func (n * network ) peerIPStatus (nodeID ids.NodeID , ip * ips.ClaimedIPPort ) (* ips.ClaimedIPPort , bool , bool , bool ) {
1029
1039
prevIP , previouslyTracked := n .peerIPs [nodeID ]
1030
1040
shouldUpdateOurIP := previouslyTracked && prevIP .Timestamp < ip .Timestamp
1031
- shouldDial := ! previouslyTracked && n .wantsConnection (nodeID )
1041
+ shouldDial := ! previouslyTracked && n .WantsConnection (nodeID )
1032
1042
return prevIP , previouslyTracked , shouldUpdateOurIP , shouldDial
1033
1043
}
1034
1044
@@ -1074,7 +1084,7 @@ func (n *network) dial(nodeID ids.NodeID, ip *trackedIP) {
1074
1084
// trackedIPs and this goroutine. This prevents a memory leak when
1075
1085
// the tracked nodeID leaves the validator set and is never able to
1076
1086
// be connected to.
1077
- if ! n .wantsConnection (nodeID ) {
1087
+ if ! n .WantsConnection (nodeID ) {
1078
1088
// Typically [n.trackedIPs[nodeID]] will already equal [ip], but
1079
1089
// the reference to [ip] is refreshed to avoid any potential
1080
1090
// race conditions before removing the entry.
0 commit comments