@@ -105,6 +105,11 @@ case class BlockFetcherState(
105
105
}
106
106
)
107
107
108
+ /**
109
+ * When bodies are requested, the response don't need to be a complete sub chain,
110
+ * even more, we could receive an empty chain and that will be considered valid. Here we just
111
+ * validate that the received bodies corresponds to an ordered subset of the requested headers.
112
+ */
108
113
def validateBodies (receivedBodies : Seq [BlockBody ]): Either [String , Seq [Block ]] =
109
114
bodiesAreOrderedSubsetOfRequested(waitingHeaders.toList, receivedBodies)
110
115
.toRight(
@@ -129,22 +134,44 @@ case class BlockFetcherState(
129
134
bodiesAreOrderedSubsetOfRequested(remainingHeaders, respondedBodies, matchedBlocks)
130
135
}
131
136
132
- def appendNewBlocks (blocks : Seq [Block ], fromPeer : PeerId ): BlockFetcherState = {
133
- val receivedHeaders = blocks.map(_.header)
134
- withPeerForBlocks(fromPeer, blocks.map(_.header.number))
135
- .copy(
136
- readyBlocks = readyBlocks.enqueue(blocks.toList),
137
- waitingHeaders = waitingHeaders.diff(receivedHeaders)
138
- )
137
+ // We could optimize this method by stopping as soon as a block is not appended.
138
+ def receiveBlocks (blocks : Seq [Block ], fromPeer : PeerId ): BlockFetcherState = {
139
+ blocks.foldLeft(this ) { case (state, block) =>
140
+ state.receiveBlock(block, fromPeer)
141
+ }
139
142
}
140
143
144
+ /**
145
+ * Currently to fill in headers we use a queue, so we if we try to process
146
+ * a block that has its header in the queue but is not the next in the line,
147
+ * we opt for not appending it.
148
+ */
149
+ def receiveBlock (block : Block , fromPeer : PeerId ): BlockFetcherState =
150
+ waitingHeaders.dequeueOption
151
+ .map { case (waitingHeader, waitingHeadersTail) =>
152
+ if (waitingHeader.hash == block.hash)
153
+ unsafeAppendNewBlock(block, fromPeer).copy(
154
+ waitingHeaders = waitingHeadersTail
155
+ )
156
+ else
157
+ this
158
+ }
159
+ .getOrElse(this )
160
+
161
+ // only succeed if there is no waiting headers.
141
162
def appendNewBlock (block : Block , fromPeer : PeerId ): BlockFetcherState =
163
+ if (waitingHeaders.isEmpty)
164
+ unsafeAppendNewBlock(block, fromPeer)
165
+ else
166
+ this
167
+
168
+ // unsafe in terms of not checking waiting headers queue
169
+ private def unsafeAppendNewBlock (block : Block , fromPeer : PeerId ): BlockFetcherState =
142
170
withPeerForBlocks(fromPeer, Seq (block.header.number))
143
171
.withPossibleNewTopAt(block.number)
144
172
.withLastBlock(block.number)
145
173
.copy(
146
- readyBlocks = readyBlocks.enqueue(block),
147
- waitingHeaders = waitingHeaders.filter(block.number != _.number)
174
+ readyBlocks = readyBlocks.enqueue(block)
148
175
)
149
176
150
177
def pickBlocks (amount : Int ): Option [(NonEmptyList [Block ], BlockFetcherState )] =
0 commit comments