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 }