p2p/discover: fix race in test

This commit is contained in:
Felix Lange 2024-05-14 13:18:47 +02:00
parent 9faae76660
commit 008ff21cdf
3 changed files with 22 additions and 18 deletions

View file

@ -79,7 +79,7 @@ type Table struct {
refreshReq chan chan struct{} refreshReq chan chan struct{}
revalResponseCh chan revalidationResponse revalResponseCh chan revalidationResponse
addNodeCh chan addNodeOp addNodeCh chan addNodeOp
addNodeHandled chan struct{} addNodeHandled chan bool
trackRequestCh chan trackRequestOp trackRequestCh chan trackRequestOp
initDone chan struct{} initDone chan struct{}
closeReq chan struct{} closeReq chan struct{}
@ -128,7 +128,7 @@ func newTable(t transport, db *enode.DB, cfg Config) (*Table, error) {
refreshReq: make(chan chan struct{}), refreshReq: make(chan chan struct{}),
revalResponseCh: make(chan revalidationResponse), revalResponseCh: make(chan revalidationResponse),
addNodeCh: make(chan addNodeOp), addNodeCh: make(chan addNodeOp),
addNodeHandled: make(chan struct{}), addNodeHandled: make(chan bool),
trackRequestCh: make(chan trackRequestOp), trackRequestCh: make(chan trackRequestOp),
initDone: make(chan struct{}), initDone: make(chan struct{}),
closeReq: make(chan struct{}), closeReq: make(chan struct{}),
@ -304,12 +304,13 @@ func (tab *Table) len() (n int) {
// list. // list.
// //
// The caller must not hold tab.mutex. // The caller must not hold tab.mutex.
func (tab *Table) addFoundNode(n *node) { func (tab *Table) addFoundNode(n *node) bool {
op := addNodeOp{node: n, isInbound: false} op := addNodeOp{node: n, isInbound: false}
select { select {
case tab.addNodeCh <- op: case tab.addNodeCh <- op:
<-tab.addNodeHandled return <-tab.addNodeHandled
case <-tab.closeReq: case <-tab.closeReq:
return false
} }
} }
@ -321,12 +322,13 @@ func (tab *Table) addFoundNode(n *node) {
// repeatedly. // repeatedly.
// //
// The caller must not hold tab.mutex. // The caller must not hold tab.mutex.
func (tab *Table) addInboundNode(n *node) { func (tab *Table) addInboundNode(n *node) bool {
op := addNodeOp{node: n, isInbound: true} op := addNodeOp{node: n, isInbound: true}
select { select {
case tab.addNodeCh <- op: case tab.addNodeCh <- op:
<-tab.addNodeHandled return <-tab.addNodeHandled
case <-tab.closeReq: case <-tab.closeReq:
return false
} }
} }
@ -370,9 +372,9 @@ loop:
case op := <-tab.addNodeCh: case op := <-tab.addNodeCh:
tab.mutex.Lock() tab.mutex.Lock()
tab.handleAddNode(op) ok := tab.handleAddNode(op)
tab.mutex.Unlock() tab.mutex.Unlock()
tab.addNodeHandled <- struct{}{} tab.addNodeHandled <- ok
case op := <-tab.trackRequestCh: case op := <-tab.trackRequestCh:
tab.handleTrackRequest(op) tab.handleTrackRequest(op)
@ -495,35 +497,36 @@ func (tab *Table) removeIP(b *bucket, ip net.IP) {
// handleAddNode adds the node in the request to the table, if there is space. // handleAddNode adds the node in the request to the table, if there is space.
// The caller must hold tab.mutex. // The caller must hold tab.mutex.
func (tab *Table) handleAddNode(req addNodeOp) { func (tab *Table) handleAddNode(req addNodeOp) bool {
if req.node.ID() == tab.self().ID() { if req.node.ID() == tab.self().ID() {
return return false
} }
// For nodes from inbound contact, there is an additional safety measure: if the table // For nodes from inbound contact, there is an additional safety measure: if the table
// is still initializing the node is not added. // is still initializing the node is not added.
if req.isInbound && !tab.isInitDone() { if req.isInbound && !tab.isInitDone() {
return return false
} }
b := tab.bucket(req.node.ID()) b := tab.bucket(req.node.ID())
if tab.bumpInBucket(b, req.node.Node) { if tab.bumpInBucket(b, req.node.Node) {
// Already in bucket, update record. // Already in bucket, update record.
return return false
} }
if len(b.entries) >= bucketSize { if len(b.entries) >= bucketSize {
// Bucket full, maybe add as replacement. // Bucket full, maybe add as replacement.
tab.addReplacement(b, req.node) tab.addReplacement(b, req.node)
return return false
} }
if !tab.addIP(b, req.node.IP()) { if !tab.addIP(b, req.node.IP()) {
// Can't add: IP limit reached. // Can't add: IP limit reached.
return return false
} }
// Add to bucket. // Add to bucket.
b.entries = append(b.entries, req.node) b.entries = append(b.entries, req.node)
b.replacements = deleteNode(b.replacements, req.node) b.replacements = deleteNode(b.replacements, req.node)
tab.nodeAdded(b, req.node) tab.nodeAdded(b, req.node)
return true
} }
// addReplacement adds n to the replacement cache of bucket b. // addReplacement adds n to the replacement cache of bucket b.
@ -573,7 +576,7 @@ func (tab *Table) nodeRemoved(b *bucket, n *node) {
func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *node { func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *node {
index := slices.IndexFunc(b.entries, func(e *node) bool { return e.ID() == id }) index := slices.IndexFunc(b.entries, func(e *node) bool { return e.ID() == id })
if index == -1 { if index == -1 {
// Entry has been removed already, don't replace it. // Entry has been removed already.
return nil return nil
} }

View file

@ -63,10 +63,10 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
<-tab.initDone <-tab.initDone
// Fill up the sender's bucket. // Fill up the sender's bucket.
tab.mutex.Lock()
replacementNodeKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8") replacementNodeKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
replacementNode := wrapNode(enode.NewV4(&replacementNodeKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99)) replacementNode := wrapNode(enode.NewV4(&replacementNodeKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99))
last := fillBucket(tab, replacementNode.ID()) last := fillBucket(tab, replacementNode.ID())
tab.mutex.Lock()
nodeEvents := newNodeEventRecorder(128) nodeEvents := newNodeEventRecorder(128)
tab.nodeAddedHook = nodeEvents.nodeAdded tab.nodeAddedHook = nodeEvents.nodeAdded
tab.nodeRemovedHook = nodeEvents.nodeRemoved tab.nodeRemovedHook = nodeEvents.nodeRemoved

View file

@ -104,8 +104,9 @@ func fillBucket(tab *Table, id enode.ID) (last *node) {
b := tab.bucket(id) b := tab.bucket(id)
for len(b.entries) < bucketSize { for len(b.entries) < bucketSize {
node := nodeAtDistance(tab.self().ID(), ld, intIP(ld)) node := nodeAtDistance(tab.self().ID(), ld, intIP(ld))
b.entries = append(b.entries, node) if !tab.addFoundNode(node) {
tab.nodeAdded(b, node) panic("node not added")
}
} }
return b.entries[bucketSize-1] return b.entries[bucketSize-1]
} }