-
Notifications
You must be signed in to change notification settings - Fork 78
[ETCM-531] Cache-based blacklist implementation [!pr] #921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
0ad0cd5
First draft of cache-based blacklist
20b0045
Fix logging output
23da5e7
Fix FastSyncSpec
68b263a
Update nix-sbt sha
29311a4
Update nix-sbt sha
72f7da8
Polish and add tests
e53e760
Rename BlackListId to BlacklistId
962fae2
Rework PeerListSupport a little bit
93d85f1
Small cleanup
c5dd35c
[ETCM-531] Turn blacklist reasons into proper types, small improvemen…
cefeea9
Make BlacklistReasonType a sealed trait
32c2c05
Only log description for blacklist reason
c1a1e99
ETCM-531 renamed minute -> minutes
248907c
ETCM-531 Reformat triggered by running sbt pp
25d6eee
ETCM-531 Fix expiration after read
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
src/main/scala/io/iohk/ethereum/blockchain/sync/Blacklist.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| package io.iohk.ethereum.blockchain.sync | ||
|
|
||
| import com.github.benmanes.caffeine.cache.Caffeine | ||
| import com.github.blemale.scaffeine.{Cache, Scaffeine} | ||
| import io.iohk.ethereum.utils.Logger | ||
|
|
||
| import scala.concurrent.duration.FiniteDuration | ||
| import scala.concurrent.duration._ | ||
| import scala.jdk.CollectionConverters._ | ||
| import scala.jdk.OptionConverters._ | ||
| import scala.jdk.DurationConverters._ | ||
|
|
||
| import Blacklist._ | ||
| import io.iohk.ethereum.network.PeerId | ||
| import io.iohk.ethereum.blockchain.sync.Blacklist.BlacklistReason.BlacklistReasonType.WrongBlockHeadersType | ||
| import io.iohk.ethereum.consensus.validators.std.StdBlockValidator.BlockError | ||
| import io.iohk.ethereum.blockchain.sync.Blacklist.BlacklistReason.BlacklistReasonType | ||
|
|
||
| trait Blacklist { | ||
| def isBlacklisted(id: BlacklistId): Boolean | ||
| def add(id: BlacklistId, duration: FiniteDuration, reason: BlacklistReason): Unit | ||
| def remove(id: BlacklistId): Unit | ||
| def keys: Set[BlacklistId] | ||
| } | ||
|
|
||
| object Blacklist { | ||
| import BlacklistReason._ | ||
| import BlacklistReasonType._ | ||
|
|
||
| trait BlacklistId { | ||
| def value: String | ||
| } | ||
|
|
||
| sealed trait BlacklistReason { | ||
AnastasiiaL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| def reasonType: BlacklistReasonType | ||
| def description: String | ||
| } | ||
| object BlacklistReason { | ||
| sealed trait BlacklistReasonType { | ||
| def code: Int | ||
| def name: String | ||
| } | ||
| object BlacklistReasonType { | ||
| case object WrongBlockHeadersType extends BlacklistReasonType { | ||
| val code: Int = 1 | ||
| val name: String = "WrongBlockHeadersType" | ||
| } | ||
| case object BlockHeaderValidationFailedType extends BlacklistReasonType { | ||
| val code: Int = 2 | ||
| val name: String = "BlockHeaderValidationFailed" | ||
| } | ||
| case object ErrorInBlockHeadersType extends BlacklistReasonType { | ||
| val code: Int = 3 | ||
| val name: String = "ErrorInBlockHeaders" | ||
| } | ||
| case object EmptyBlockBodiesType extends BlacklistReasonType { | ||
| val code: Int = 4 | ||
| val name: String = "EmptyBlockBodies" | ||
| } | ||
| case object BlockBodiesNotMatchingHeadersType extends BlacklistReasonType { | ||
| val code: Int = 5 | ||
| val name: String = "BlockBodiesNotMatchingHeaders" | ||
| } | ||
| case object EmptyReceiptsType extends BlacklistReasonType { | ||
| val code: Int = 6 | ||
| val name: String = "EmptyReceipts" | ||
| } | ||
| case object InvalidReceiptsType extends BlacklistReasonType { | ||
| val code: Int = 7 | ||
| val name: String = "InvalidReceipts" | ||
| } | ||
| case object RequestFailedType extends BlacklistReasonType { | ||
| val code: Int = 8 | ||
| val name: String = "RequestFailed" | ||
| } | ||
| case object PeerActorTerminatedType extends BlacklistReasonType { | ||
| val code: Int = 9 | ||
| val name: String = "PeerActorTerminated" | ||
| } | ||
| } | ||
|
|
||
| case object WrongBlockHeaders extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = WrongBlockHeadersType | ||
| val description: String = "Wrong blockheaders response (empty or not chain forming)" | ||
| } | ||
| case object BlockHeaderValidationFailed extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = BlockHeaderValidationFailedType | ||
| val description: String = "Block header validation failed" | ||
| } | ||
| case object ErrorInBlockHeaders extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = ErrorInBlockHeadersType | ||
| val description: String = "Error in block headers response" | ||
| } | ||
| final case class EmptyBlockBodies(knownHashes: Seq[String]) extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = EmptyBlockBodiesType | ||
| val description: String = s"Got empty block bodies response for known hashes: $knownHashes" | ||
| } | ||
| case object BlockBodiesNotMatchingHeaders extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = BlockBodiesNotMatchingHeadersType | ||
| val description = "Block bodies not matching block headers" | ||
| } | ||
| final case class EmptyReceipts(knownHashes: Seq[String]) extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = EmptyReceiptsType | ||
| val description: String = s"Got empty receipts for known hashes: $knownHashes" | ||
| } | ||
| final case class InvalidReceipts(knownHashes: Seq[String], error: BlockError) extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = InvalidReceiptsType | ||
| val description: String = s"Got invalid receipts for known hashes: $knownHashes due to: $error" | ||
| } | ||
| final case class RequestFailed(error: String) extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = RequestFailedType | ||
| val description: String = s"Request failed with error: $error" | ||
| } | ||
| case object PeerActorTerminated extends BlacklistReason { | ||
| val reasonType: BlacklistReasonType = PeerActorTerminatedType | ||
| val description: String = "Peer actor terminated" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| final case class CacheBasedBlacklist(cache: Cache[BlacklistId, BlacklistReasonType]) extends Blacklist with Logger { | ||
|
|
||
| import CacheBasedBlacklist._ | ||
|
|
||
| override def isBlacklisted(id: BlacklistId): Boolean = cache.getIfPresent(id).isDefined | ||
|
|
||
| override def add(id: BlacklistId, duration: FiniteDuration, reason: BlacklistReason): Unit = { | ||
| log.info("Blacklisting peer [{}] for {}. Reason: {}", id, duration, reason.description) | ||
| cache.policy().expireVariably().toScala match { | ||
| case Some(varExpiration) => varExpiration.put(id, reason.reasonType, duration.toJava) | ||
| case None => | ||
| log.warn(customExpirationError(id)) | ||
| cache.put(id, reason.reasonType) | ||
| } | ||
| } | ||
| override def remove(id: BlacklistId): Unit = cache.invalidate(id) | ||
|
|
||
| override def keys: Set[BlacklistId] = cache.underlying.asMap().keySet().asScala.toSet | ||
| } | ||
|
|
||
| object CacheBasedBlacklist { | ||
|
|
||
| def customExpirationError(id: BlacklistId): String = | ||
| s"Unexpected error while adding peer [${id.value}] to blacklist using custom expiration time. Falling back to default expiration." | ||
|
|
||
| def empty(maxSize: Int): CacheBasedBlacklist = { | ||
| val cache = | ||
| Scaffeine() | ||
| .expireAfter[BlacklistId, BlacklistReasonType]( | ||
| create = (_, _) => 60.minutes, | ||
| update = (_, _, _) => 60.minutes, | ||
| read = (_, _, duration) => duration // read access should not change the expiration time | ||
| ) // required to enable VarExpiration policy (i.e. set custom expiration time per element) | ||
| .maximumSize( | ||
| maxSize | ||
| ) // uses Window TinyLfu eviction policy, see https://github.com/ben-manes/caffeine/wiki/Efficiency | ||
| .build[BlacklistId, BlacklistReasonType]() | ||
| CacheBasedBlacklist(cache) | ||
| } | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.