github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/node/internal/cache.go (about)

     1  package internal
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/libp2p/go-libp2p/core/peer"
     8  	"github.com/rs/zerolog"
     9  
    10  	"github.com/onflow/flow-go/model/flow"
    11  	"github.com/onflow/flow-go/module"
    12  	herocache "github.com/onflow/flow-go/module/mempool/herocache/backdata"
    13  	"github.com/onflow/flow-go/module/mempool/herocache/backdata/heropool"
    14  	"github.com/onflow/flow-go/module/mempool/stdmap"
    15  	"github.com/onflow/flow-go/network"
    16  )
    17  
    18  var (
    19  	ErrDisallowCacheEntityNotFound = errors.New("disallow list cache entity not found")
    20  )
    21  
    22  // DisallowListCache is the disallow-list cache. It is used to keep track of the disallow-listed peers and the reasons for it.
    23  type DisallowListCache struct {
    24  	c *stdmap.Backend
    25  }
    26  
    27  // NewDisallowListCache creates a new disallow-list cache. The cache is backed by a stdmap.Backend.
    28  // Args:
    29  // - sizeLimit: the size limit of the cache, i.e., the maximum number of records that the cache can hold, recommended size is 100 * number of authorized nodes.
    30  // - logger: the logger used by the cache.
    31  // - collector: the metrics collector used by the cache.
    32  // Returns:
    33  // - *DisallowListCache: the created cache.
    34  func NewDisallowListCache(sizeLimit uint32, logger zerolog.Logger, collector module.HeroCacheMetrics) *DisallowListCache {
    35  	backData := herocache.NewCache(sizeLimit,
    36  		herocache.DefaultOversizeFactor,
    37  		heropool.LRUEjection,
    38  		logger.With().Str("mempool", "disallow-list-records").Logger(),
    39  		collector)
    40  
    41  	return &DisallowListCache{
    42  		c: stdmap.NewBackend(stdmap.WithBackData(backData)),
    43  	}
    44  }
    45  
    46  // IsDisallowListed determines whether the given peer is disallow-listed for any reason.
    47  // Args:
    48  // - peerID: the peer to check.
    49  // Returns:
    50  // - []network.DisallowListedCause: the list of causes for which the given peer is disallow-listed. If the peer is not disallow-listed for any reason,
    51  // a nil slice is returned.
    52  // - bool: true if the peer is disallow-listed for any reason, false otherwise.
    53  func (d *DisallowListCache) IsDisallowListed(peerID peer.ID) ([]network.DisallowListedCause, bool) {
    54  	entity, exists := d.c.ByID(makeId(peerID))
    55  	if !exists {
    56  		return nil, false
    57  	}
    58  
    59  	dEntity := mustBeDisallowListEntity(entity)
    60  	if len(dEntity.causes) == 0 {
    61  		return nil, false
    62  	}
    63  
    64  	causes := make([]network.DisallowListedCause, 0, len(dEntity.causes))
    65  	for c := range dEntity.causes {
    66  		causes = append(causes, c)
    67  	}
    68  	return causes, true
    69  }
    70  
    71  // DisallowFor disallow-lists a peer for a cause.
    72  // Args:
    73  // - peerID: the peerID of the peer to be disallow-listed.
    74  // - cause: the cause for disallow-listing the peer.
    75  // Returns:
    76  // - []network.DisallowListedCause: the list of causes for which the peer is disallow-listed.
    77  // - error: if the operation fails, error is irrecoverable.
    78  func (d *DisallowListCache) DisallowFor(peerID peer.ID, cause network.DisallowListedCause) ([]network.DisallowListedCause, error) {
    79  	entityId := makeId(peerID)
    80  	initLogic := func() flow.Entity {
    81  		return &disallowListCacheEntity{
    82  			peerID:   peerID,
    83  			causes:   make(map[network.DisallowListedCause]struct{}),
    84  			entityId: entityId,
    85  		}
    86  	}
    87  	adjustLogic := func(entity flow.Entity) flow.Entity {
    88  		dEntity := mustBeDisallowListEntity(entity)
    89  		dEntity.causes[cause] = struct{}{}
    90  		return dEntity
    91  	}
    92  	adjustedEntity, adjusted := d.c.AdjustWithInit(entityId, adjustLogic, initLogic)
    93  	if !adjusted {
    94  		return nil, fmt.Errorf("failed to disallow list peer %s for cause %s", peerID, cause)
    95  	}
    96  
    97  	dEntity := mustBeDisallowListEntity(adjustedEntity)
    98  	// returning a deep copy of causes (to avoid being mutated externally).
    99  	updatedCauses := make([]network.DisallowListedCause, 0, len(dEntity.causes))
   100  	for c := range dEntity.causes {
   101  		updatedCauses = append(updatedCauses, c)
   102  	}
   103  
   104  	return updatedCauses, nil
   105  }
   106  
   107  // AllowFor removes a cause from the disallow list cache entity for the peerID.
   108  // Args:
   109  // - peerID: the peerID of the peer to be allow-listed.
   110  // - cause: the cause for allow-listing the peer.
   111  // Returns:
   112  // - the list of causes for which the peer is disallow-listed.
   113  // - error if the entity for the peerID is not found in the cache it returns ErrDisallowCacheEntityNotFound, which is a benign error.
   114  func (d *DisallowListCache) AllowFor(peerID peer.ID, cause network.DisallowListedCause) []network.DisallowListedCause {
   115  	adjustedEntity, adjusted := d.c.Adjust(makeId(peerID), func(entity flow.Entity) flow.Entity {
   116  		dEntity := mustBeDisallowListEntity(entity)
   117  		delete(dEntity.causes, cause)
   118  		return dEntity
   119  	})
   120  
   121  	if !adjusted {
   122  		// if the entity is not found in the cache, we return an empty list.
   123  		// we don't return a nil to be consistent with the case that entity is found but the list of causes is empty.
   124  		return make([]network.DisallowListedCause, 0)
   125  	}
   126  
   127  	dEntity := mustBeDisallowListEntity(adjustedEntity)
   128  	// returning a deep copy of causes (to avoid being mutated externally).
   129  	causes := make([]network.DisallowListedCause, 0, len(dEntity.causes))
   130  	for c := range dEntity.causes {
   131  		causes = append(causes, c)
   132  	}
   133  	return causes
   134  }
   135  
   136  // mustBeDisallowListEntity is a helper function for type assertion of the flow.Entity to disallowListCacheEntity.
   137  // It panics if the type assertion fails.
   138  // Args:
   139  // - entity: the flow.Entity to be type asserted.
   140  // Returns:
   141  // - the disallowListCacheEntity.
   142  func mustBeDisallowListEntity(entity flow.Entity) *disallowListCacheEntity {
   143  	dEntity, ok := entity.(*disallowListCacheEntity)
   144  	if !ok {
   145  		// this should never happen, unless there is a bug. We should crash the node and do not proceed.
   146  		panic(fmt.Errorf("disallow list cache entity is not of type disallowListCacheEntity, got: %T", entity))
   147  	}
   148  	return dEntity
   149  }