Skip to content

Commit 04208b0

Browse files
enriquerodbebiandrattirobinraju1015bit
authored
[ETCM-313] and [ETCM-316]: Header skeleton using new branch resolver (#892)
* create actor FastSyncBranchResolver * ETCM-313 Download skeleton and then batch headers in parallel * Implement HeaderSkeleton class * Validate if skeleton header matches downloaded batch * Improve validations * Handle wrong skeleton from master peer * Fix incorrect example * Validate PoW of skeleton headers * Fix bugs found with tests * Add call to the branch resolver * Add missing config entries * Fix unit tests * Apply scalafmt * Cleanup tests * Fastsync: stick with the same master peer while requesting skeleton headers. * [ETCM-313] Integrate branch resolver actor with fast sync * [ETCM-313] Fix integration tests, format error messages * create actor FastSyncBranchResolver * create actor FastSyncBranchResolver * [ETCM-316] Fast-sync branch resolver (#887) * init new actor, FastSyncBranchResolver * added searching mode in fast sync branch resolver * fix style * add schedule when don't get peers * Added ut in class FastSyncBranchResolver * change messages * fix case object... * add new unit test * add new unit test * change name getFirstCommonBlock * change name to batch * init new actor, FastSyncBranchResolver * added searching mode in fast sync branch resolver * fix style * add schedule when don't get peers * Added ut in class FastSyncBranchResolver * change messages * fix case object... * add new unit test * add new unit test * create actor FastSyncBranchResolver * change name getFirstCommonBlock * change name to batch * Reformat triggered by sbt pp * Cleanup and simplify * Handle error cases * Fix tests * [ETCM-316] Add more tests and fix binary search logic * [ETCM-316] Finish tests for branch resolving * [ETCM-316] Cleanup * [ETCM-316] Small test improvements * [ETCM-316] Log binary search state * [ETCM-316] Move some logging to improve readability * [ETCM-316] Remove unneeded errors and reformat * [ETCM-316] Handle branch resolution failure * [ETCM-316] Address PR comments * [ETCM-316] Remove unnecessary string interpolation Co-authored-by: Petra Bierleutgeb <[email protected]> * [ETCM-313] Reworked header skeleton (still needs refactoring) * [ETCM-313] Remove empty method * Fix SyncController tests, add more logging * Remove logging that broke integration tests (timeout) * [ETCM-313] More refactorings * [ETCM-313] Fix integration tests * [ETCM-313] Re-request header skeleton in case of errors * [ETCM-313] Remove skeleton handler name * [ETCM-313] Small fixes and better logs * [ETCM-313] Update the default number of requested block headers to not be higher than the default max number of headers returned * [ETCM-313] Adapt branchresolver recent blocks request. Co-authored-by: Maximiliano Biandratti <[email protected]> Co-authored-by: Robin Raju <[email protected]> Co-authored-by: Petra Bierleutgeb <[email protected]> Co-authored-by: biandratti <[email protected]> Co-authored-by: Petra Bierleutgeb <[email protected]>
1 parent 1ab30ce commit 04208b0

32 files changed

+1784
-210
lines changed

src/ets/scala/io/iohk/ethereum/ets/blockchain/EthashTestBlockHeaderValidator.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class EthashTestBlockHeaderValidator(blockchainConfig: BlockchainConfig) extends
1515

1616
protected def difficulty: DifficultyCalculator = DifficultyCalculator(blockchainConfig)
1717

18-
def validateEvenMore(blockHeader: BlockHeader, parentHeader: BlockHeader): Either[BlockHeaderError, BlockHeaderValid] =
18+
override def validateEvenMore(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeaderValid] =
1919
Right(BlockHeaderValid)
2020

2121
}

src/it/scala/io/iohk/ethereum/sync/FastSyncItSpec.scala

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,49 +59,53 @@ class FastSyncItSpec extends FlatSpecBase with Matchers with BeforeAndAfterAll {
5959

6060
it should "sync blockchain with state nodes when peer do not response with full responses" in
6161
customTestCaseResourceM(
62-
FakePeer.start3FakePeersRes(
62+
FakePeer.start4FakePeersRes(
6363
fakePeerCustomConfig2 = FakePeerCustomConfig(HostConfig()),
6464
fakePeerCustomConfig3 = FakePeerCustomConfig(HostConfig())
6565
)
66-
) { case (peer1, peer2, peer3) =>
66+
) { case (peer1, peer2, peer3, peer4) =>
6767
for {
6868
_ <- peer2.importBlocksUntil(1000)(updateStateAtBlock(500))
6969
_ <- peer3.importBlocksUntil(1000)(updateStateAtBlock(500))
70-
_ <- peer1.connectToPeers(Set(peer2.node, peer3.node))
70+
_ <- peer4.importBlocksUntil(1000)(updateStateAtBlock(500))
71+
72+
_ <- peer1.connectToPeers(Set(peer2.node, peer3.node, peer4.node))
7173
_ <- peer1.startFastSync().delayExecution(50.milliseconds)
7274
_ <- peer1.waitForFastSyncFinish()
7375
} yield {
7476
val trie = peer1.getBestBlockTrie()
7577
val synchronizingPeerHaveAllData = peer1.containsExpectedDataUpToAccountAtBlock(1000, 500)
76-
// due to the fact that function generating state is deterministic both peer2 and peer3 ends up with exactly same
78+
// due to the fact that function generating state is deterministic both peer3 and peer4 ends up with exactly same
7779
// state, so peer1 can get whole trie from both of them.
78-
assert(peer1.bl.getBestBlockNumber() == peer2.bl.getBestBlockNumber() - peer2.testSyncConfig.pivotBlockOffset)
7980
assert(peer1.bl.getBestBlockNumber() == peer3.bl.getBestBlockNumber() - peer3.testSyncConfig.pivotBlockOffset)
81+
assert(peer1.bl.getBestBlockNumber() == peer4.bl.getBestBlockNumber() - peer4.testSyncConfig.pivotBlockOffset)
8082
assert(trie.isDefined)
8183
assert(synchronizingPeerHaveAllData)
8284
}
8385
}
8486

8587
it should "sync blockchain with state nodes when one of the peers send empty state responses" in
8688
customTestCaseResourceM(
87-
FakePeer.start3FakePeersRes(
89+
FakePeer.start4FakePeersRes(
8890
fakePeerCustomConfig2 = FakePeerCustomConfig(HostConfig()),
8991
fakePeerCustomConfig3 = FakePeerCustomConfig(HostConfig().copy(maxMptComponentsPerMessage = 0))
9092
)
91-
) { case (peer1, peer2, peer3) =>
93+
) { case (peer1, peer2, peer3, peer4) =>
9294
for {
9395
_ <- peer2.importBlocksUntil(1000)(updateStateAtBlock(500))
9496
_ <- peer3.importBlocksUntil(1000)(updateStateAtBlock(500))
95-
_ <- peer1.connectToPeers(Set(peer2.node, peer3.node))
97+
_ <- peer4.importBlocksUntil(1000)(updateStateAtBlock(500))
98+
99+
_ <- peer1.connectToPeers(Set(peer2.node, peer3.node, peer4.node))
96100
_ <- peer1.startFastSync().delayExecution(50.milliseconds)
97101
_ <- peer1.waitForFastSyncFinish()
98102
} yield {
99103
val trie = peer1.getBestBlockTrie()
100104
val synchronizingPeerHaveAllData = peer1.containsExpectedDataUpToAccountAtBlock(1000, 500)
101-
// due to the fact that function generating state is deterministic both peer2 and peer3 ends up with exactly same
105+
// due to the fact that function generating state is deterministic both peer3 and peer4 ends up with exactly same
102106
// state, so peer1 can get whole trie from both of them.
103-
assert(peer1.bl.getBestBlockNumber() == peer2.bl.getBestBlockNumber() - peer2.testSyncConfig.pivotBlockOffset)
104107
assert(peer1.bl.getBestBlockNumber() == peer3.bl.getBestBlockNumber() - peer3.testSyncConfig.pivotBlockOffset)
108+
assert(peer1.bl.getBestBlockNumber() == peer4.bl.getBestBlockNumber() - peer4.testSyncConfig.pivotBlockOffset)
105109
assert(trie.isDefined)
106110
assert(synchronizingPeerHaveAllData)
107111
}

src/it/scala/io/iohk/ethereum/sync/util/CommonFakePeer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ abstract class CommonFakePeer(peerName: String, fakePeerCustomConfig: FakePeerCu
222222
startRetryInterval = 50.milliseconds,
223223
nodesPerRequest = 200,
224224
maxTargetDifference = 1,
225-
syncRetryInterval = 50.milliseconds
225+
syncRetryInterval = 50.milliseconds,
226+
blacklistDuration = 100.seconds
226227
)
227228

228229
lazy val broadcaster = new BlockBroadcast(etcPeerManager)

src/main/resources/application.conf

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,9 @@ mantis {
369369
max-concurrent-requests = 50
370370

371371
# Requested number of block headers when syncing from other peers
372-
block-headers-per-request = 200
372+
# Will cause an error if it's higher than max-blocks-headers-per-message of the peer we're requesting from,
373+
# so this number should not be set very high.
374+
block-headers-per-request = 100
373375

374376
# Requested number of block bodies when syncing from other peers
375377
block-bodies-per-request = 128
@@ -469,6 +471,10 @@ mantis {
469471
# (peer.bestKnownBlock - pivot-block-offset) - node.curentPivotBlock > max-pivot-age
470472
# it fast sync pivot block has become stale and it needs update
471473
max-pivot-block-age = 96
474+
475+
# Maximum number of retries performed by fast sync when the master peer sends invalid block headers.
476+
# On reaching this limit, it will perform branch resolving.
477+
fast-sync-max-batch-retries = 5
472478
}
473479

474480
pruning {

src/main/scala/io/iohk/ethereum/blockchain/sync/Blacklist.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ object Blacklist {
9393

9494
case object WrongBlockHeaders extends BlacklistReason {
9595
val reasonType: BlacklistReasonType = WrongBlockHeadersType
96-
val description: String = "Wrong blockheaders response (empty or not chain forming)"
96+
val description: String = "Wrong blockheaders response: Peer didn't respond with requested block headers."
9797
}
9898
case object BlockHeaderValidationFailed extends BlacklistReason {
9999
val reasonType: BlacklistReasonType = BlockHeaderValidationFailedType

src/main/scala/io/iohk/ethereum/blockchain/sync/PeerListSupportNg.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ trait PeerListSupportNg { self: Actor with ActorLogging =>
1717

1818
private implicit val ec: ExecutionContext = context.dispatcher
1919

20+
protected val bigIntReverseOrdering: Ordering[BigInt] = Ordering[BigInt].reverse
21+
2022
def etcPeerManager: ActorRef
2123
def peerEventBus: ActorRef
2224
def blacklist: Blacklist
@@ -44,6 +46,9 @@ trait PeerListSupportNg { self: Actor with ActorLogging =>
4446

4547
def getPeerById(peerId: PeerId): Option[Peer] = handshakedPeers.get(peerId).map(_.peer)
4648

49+
def getPeerWithHighestBlock: Option[PeerWithInfo] =
50+
peersToDownloadFrom.values.toList.sortBy(_.peerInfo.maxBlockNumber)(bigIntReverseOrdering).headOption
51+
4752
def blacklistIfHandshaked(peerId: PeerId, duration: FiniteDuration, reason: BlacklistReason): Unit =
4853
handshakedPeers.get(peerId).foreach(_ => blacklist.add(peerId, duration, reason))
4954

src/main/scala/io/iohk/ethereum/blockchain/sync/PeerRequestHandler.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ class PeerRequestHandler[RequestMsg <: Message, ResponseMsg <: Message: ClassTag
2424

2525
import PeerRequestHandler._
2626

27-
val initiator: ActorRef = context.parent
27+
private val initiator: ActorRef = context.parent
2828

29-
val timeout: Cancellable = scheduler.scheduleOnce(responseTimeout, self, Timeout)
29+
private val timeout: Cancellable = scheduler.scheduleOnce(responseTimeout, self, Timeout)
3030

31-
val startTime: Long = System.currentTimeMillis()
31+
private val startTime: Long = System.currentTimeMillis()
3232

3333
private def subscribeMessageClassifier = MessageClassifier(Set(responseMsgCode), PeerSelector.WithId(peer.id))
3434

35-
def timeTakenSoFar(): Long = System.currentTimeMillis() - startTime
35+
private def timeTakenSoFar(): Long = System.currentTimeMillis() - startTime
3636

3737
override def preStart(): Unit = {
3838
etcPeerManager ! EtcPeerManagerActor.SendMessage(toSerializable(requestMsg), peer.id)
@@ -79,8 +79,8 @@ object PeerRequestHandler {
7979
)(implicit scheduler: Scheduler, toSerializable: RequestMsg => MessageSerializable): Props =
8080
Props(new PeerRequestHandler(peer, responseTimeout, etcPeerManager, peerEventBus, requestMsg, responseMsgCode))
8181

82-
case class RequestFailed(peer: Peer, reason: String)
83-
case class ResponseReceived[T](peer: Peer, response: T, timeTaken: Long)
82+
final case class RequestFailed(peer: Peer, reason: String)
83+
final case class ResponseReceived[T](peer: Peer, response: T, timeTaken: Long)
8484

8585
private case object Timeout
8686
}

0 commit comments

Comments
 (0)