Skip to content

Commit 3e6d236

Browse files
committed
[ETCM-283] Add block bodies validation in block fetcher
1 parent 3b2198e commit 3e6d236

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

src/main/scala/io/iohk/ethereum/blockchain/sync/regular/BlockFetcher.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,15 @@ class BlockFetcher(
143143
log.debug("Received {} block bodies that will be ignored", bodies.size)
144144
state.withBodiesFetchReceived
145145
} else {
146-
log.debug("Fetched {} block bodies", bodies.size)
147-
state.withBodiesFetchReceived.addBodies(peer, bodies)
146+
state.validateBodies(bodies) match {
147+
case Left(err) =>
148+
peersClient ! BlacklistPeer(peer.id, err)
149+
state.withBodiesFetchReceived
150+
case Right(newBlocks) =>
151+
log.debug("Fetched {} block bodies", newBlocks.size)
152+
state.withBodiesFetchReceived.appendNewBlocks(newBlocks, peer.id)
153+
}
148154
}
149-
150155
fetchBlocks(newState)
151156
case RetryBodiesRequest if state.isFetchingBodies =>
152157
log.debug("Time-out occurred while waiting for bodies")

src/main/scala/io/iohk/ethereum/blockchain/sync/regular/BlockFetcherState.scala

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ package io.iohk.ethereum.blockchain.sync.regular
33
import akka.actor.ActorRef
44
import akka.util.ByteString
55
import cats.data.NonEmptyList
6+
//FIXME: By using this class, we are coupling sync process with a specific consensus (the standard one).
7+
import io.iohk.ethereum.consensus.validators.std.StdBlockValidator
68
import io.iohk.ethereum.domain.{Block, BlockHeader, BlockBody, HeadersSeq}
7-
import io.iohk.ethereum.network.{Peer, PeerId}
9+
import io.iohk.ethereum.network.PeerId
810
import io.iohk.ethereum.network.p2p.messages.PV62.BlockHash
911
import BlockFetcherState._
1012
import cats.syntax.either._
1113
import cats.syntax.option._
1214

1315
import scala.collection.immutable.Queue
16+
import scala.annotation.tailrec
1417

1518
// scalastyle:off number.of.methods
1619
/**
@@ -101,14 +104,36 @@ case class BlockFetcherState(
101104
}
102105
)
103106

104-
def addBodies(peer: Peer, bodies: Seq[BlockBody]): BlockFetcherState = {
105-
val (matching, waiting) = waitingHeaders.splitAt(bodies.length)
106-
val blocks = matching.zip(bodies).map((Block.apply _).tupled)
107+
def validateBodies(receivedBodies: Seq[BlockBody]): Either[String, Seq[Block]] =
108+
bodiesAreOrderedSubsetOfRequested(waitingHeaders.toList, receivedBodies)
109+
.toRight(
110+
"Received unrequested bodies"
111+
)
112+
113+
// Checks that the received block bodies are an ordered subset of the ones requested
114+
@tailrec
115+
private def bodiesAreOrderedSubsetOfRequested(
116+
requestedHeaders: Seq[BlockHeader],
117+
respondedBodies: Seq[BlockBody],
118+
matchedBlocks: Seq[Block] = Nil
119+
): Option[Seq[Block]] =
120+
(requestedHeaders, respondedBodies) match {
121+
case (Seq(), _ +: _) => None
122+
case (_, Seq()) => Some(matchedBlocks)
123+
case (header +: remainingHeaders, body +: remainingBodies) =>
124+
val doMatch = StdBlockValidator.validateHeaderAndBody(header, body).isRight
125+
if (doMatch)
126+
bodiesAreOrderedSubsetOfRequested(remainingHeaders, remainingBodies, matchedBlocks :+ Block(header, body))
127+
else
128+
bodiesAreOrderedSubsetOfRequested(remainingHeaders, respondedBodies, matchedBlocks)
129+
}
107130

108-
withPeerForBlocks(peer.id, blocks.map(_.header.number))
131+
def appendNewBlocks(blocks: Seq[Block], fromPeer: PeerId): BlockFetcherState = {
132+
val receivedHeaders = blocks.map(_.header)
133+
withPeerForBlocks(fromPeer, blocks.map(_.header.number))
109134
.copy(
110-
readyBlocks = readyBlocks.enqueue(blocks),
111-
waitingHeaders = waiting
135+
readyBlocks = readyBlocks.enqueue(blocks.toList),
136+
waitingHeaders = waitingHeaders.diff(receivedHeaders)
112137
)
113138
}
114139

0 commit comments

Comments
 (0)