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  }