github.com/ethersphere/bee/v2@v2.2.0/pkg/blocker/blocker.go (about) 1 // Copyright 2021 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package blocker 6 7 import ( 8 "fmt" 9 "sync" 10 "time" 11 12 "github.com/ethersphere/bee/v2/pkg/log" 13 "github.com/ethersphere/bee/v2/pkg/p2p" 14 "github.com/ethersphere/bee/v2/pkg/swarm" 15 "go.uber.org/atomic" 16 ) 17 18 // loggerName is the tree path name of the logger for this package. 19 const loggerName = "blocker" 20 21 // sequencerResolution represents monotonic sequencer resolution. 22 // It must be in the time.Duration base form without a multiplier. 23 var sequencerResolution = time.Second 24 25 type peer struct { 26 blockAfter uint64 27 address swarm.Address 28 } 29 30 type Blocker struct { 31 sequence atomic.Uint64 // Monotonic clock. 32 mu sync.Mutex 33 blocklister p2p.Blocklister 34 flagTimeout time.Duration // how long before blocking a flagged peer 35 blockDuration time.Duration // how long to blocklist a bad peer 36 peers map[string]*peer 37 logger log.Logger 38 wakeupCh chan struct{} 39 quit chan struct{} 40 closeWg sync.WaitGroup 41 blocklistCallback func(swarm.Address) 42 } 43 44 func New(blocklister p2p.Blocklister, flagTimeout, blockDuration, wakeUpTime time.Duration, callback func(swarm.Address), logger log.Logger) *Blocker { 45 if flagTimeout <= sequencerResolution { 46 panic(fmt.Errorf("flag timeout %v cannot be equal to or lower then the sequencer resolution %v", flagTimeout, sequencerResolution)) 47 } 48 if wakeUpTime < sequencerResolution { 49 panic(fmt.Errorf("wakeup time %v cannot be lower then the clock sequencer resolution %v", wakeUpTime, sequencerResolution)) 50 } 51 52 b := &Blocker{ 53 blocklister: blocklister, 54 flagTimeout: flagTimeout, 55 blockDuration: blockDuration, 56 peers: map[string]*peer{}, 57 wakeupCh: make(chan struct{}), 58 quit: make(chan struct{}), 59 logger: logger.WithName(loggerName).Register(), 60 closeWg: sync.WaitGroup{}, 61 blocklistCallback: callback, 62 } 63 64 b.closeWg.Add(1) 65 go func() { 66 defer b.closeWg.Done() 67 for { 68 select { 69 case <-b.quit: 70 return 71 case <-time.After(sequencerResolution): 72 if b.blocklister.NetworkStatus() == p2p.NetworkStatusAvailable { 73 b.sequence.Inc() 74 } 75 } 76 } 77 }() 78 79 b.closeWg.Add(1) 80 go func() { 81 defer b.closeWg.Done() 82 for { 83 select { 84 case <-time.After(wakeUpTime): 85 b.block() 86 case <-b.quit: 87 return 88 } 89 } 90 }() 91 92 return b 93 } 94 95 func (b *Blocker) block() { 96 b.mu.Lock() 97 defer b.mu.Unlock() 98 99 for key, peer := range b.peers { 100 select { 101 case <-b.quit: 102 return 103 default: 104 } 105 106 if 0 < peer.blockAfter && peer.blockAfter < b.sequence.Load() { 107 if err := b.blocklister.Blocklist(peer.address, b.blockDuration, "blocker: flag timeout"); err != nil { 108 b.logger.Warning("blocking peer failed", "peer_address", peer.address, "error", err) 109 } 110 if b.blocklistCallback != nil { 111 b.blocklistCallback(peer.address) 112 } 113 delete(b.peers, key) 114 } 115 } 116 } 117 118 func (b *Blocker) Flag(addr swarm.Address) { 119 if b.blocklister.NetworkStatus() != p2p.NetworkStatusAvailable { 120 return 121 } 122 123 b.mu.Lock() 124 defer b.mu.Unlock() 125 126 if _, ok := b.peers[addr.ByteString()]; !ok { 127 b.peers[addr.ByteString()] = &peer{ 128 blockAfter: b.sequence.Load() + uint64(b.flagTimeout/sequencerResolution), 129 address: addr, 130 } 131 } 132 } 133 134 func (b *Blocker) Unflag(addr swarm.Address) { 135 b.mu.Lock() 136 defer b.mu.Unlock() 137 138 delete(b.peers, addr.ByteString()) 139 } 140 141 func (b *Blocker) PruneUnseen(seen []swarm.Address) { 142 isSeen := func(addr string) bool { 143 for _, a := range seen { 144 if a.ByteString() == addr { 145 return true 146 } 147 } 148 return false 149 } 150 151 b.mu.Lock() 152 defer b.mu.Unlock() 153 for a := range b.peers { 154 if !isSeen(a) { 155 delete(b.peers, a) 156 } 157 } 158 } 159 160 // Close will exit the worker loop. 161 // must be called only once. 162 func (b *Blocker) Close() error { 163 close(b.quit) 164 b.closeWg.Wait() 165 return nil 166 }