-
Notifications
You must be signed in to change notification settings - Fork 130
[NC-1273] Start of fast sync downloader #613
Changes from 25 commits
f2d5f68
cb8cfb2
8b10aed
6f3abb0
27b044c
d813ffc
6ecea9c
89b134d
bf5188e
f67c9f9
815d385
c1e1c2f
b7266d1
32bdd13
ae0a318
7f42f9a
c43524a
5329db7
1ac4f4f
3598197
6a7fac0
1bdc38d
1ba9c7f
599af66
d0b6fac
0f2ec34
e733789
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
* Copyright 2019 ConsenSys AG. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | ||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations under the License. | ||
*/ | ||
package tech.pegasys.pantheon.ethereum.eth.sync.fastsync; | ||
|
||
import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.CHAIN_TOO_SHORT; | ||
import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.NO_PEERS_AVAILABLE; | ||
|
||
import tech.pegasys.pantheon.ethereum.ProtocolContext; | ||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; | ||
import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; | ||
import tech.pegasys.pantheon.ethereum.eth.manager.EthScheduler; | ||
import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; | ||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.WaitForPeerTask; | ||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.WaitForPeersTask; | ||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; | ||
import tech.pegasys.pantheon.metrics.LabelledMetric; | ||
import tech.pegasys.pantheon.metrics.OperationTimer; | ||
import tech.pegasys.pantheon.util.ExceptionUtils; | ||
|
||
import java.util.OptionalLong; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.TimeoutException; | ||
|
||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
|
||
public class FastSyncActions<C> { | ||
|
||
private static final Logger LOG = LogManager.getLogger(); | ||
private final SynchronizerConfiguration syncConfig; | ||
private final ProtocolSchedule<C> protocolSchedule; | ||
private final ProtocolContext<C> protocolContext; | ||
private final EthContext ethContext; | ||
private final LabelledMetric<OperationTimer> ethTasksTimer; | ||
|
||
public FastSyncActions( | ||
final SynchronizerConfiguration syncConfig, | ||
final ProtocolSchedule<C> protocolSchedule, | ||
final ProtocolContext<C> protocolContext, | ||
final EthContext ethContext, | ||
final LabelledMetric<OperationTimer> ethTasksTimer) { | ||
this.syncConfig = syncConfig; | ||
this.protocolSchedule = protocolSchedule; | ||
this.protocolContext = protocolContext; | ||
this.ethContext = ethContext; | ||
this.ethTasksTimer = ethTasksTimer; | ||
} | ||
|
||
public CompletableFuture<Void> waitForSuitablePeers() { | ||
final WaitForPeersTask waitForPeersTask = | ||
WaitForPeersTask.create( | ||
ethContext, syncConfig.getFastSyncMinimumPeerCount(), ethTasksTimer); | ||
|
||
final EthScheduler scheduler = ethContext.getScheduler(); | ||
final CompletableFuture<Void> result = new CompletableFuture<>(); | ||
scheduler | ||
.timeout(waitForPeersTask, syncConfig.getFastSyncMaximumPeerWaitTime()) | ||
.handle( | ||
(waitResult, error) -> { | ||
if (ExceptionUtils.rootCause(error) instanceof TimeoutException) { | ||
if (ethContext.getEthPeers().availablePeerCount() > 0) { | ||
LOG.warn( | ||
"Fast sync timed out before minimum peer count was reached. Continuing with reduced peers."); | ||
result.complete(null); | ||
} else { | ||
waitForAnyPeer() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I would just have the action fail at this point and get handled at a higher level. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem with that is you wind up having to duplicate most of this
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking we'd just cut the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah it's a bit of a toss up. The penalty for not waiting is a month long full sync so it's unlikely the user would want that, but maybe they'd want to be strict about waiting for a minimum number of peers. We probably should also allow the user to specifically set a pivot block by hash and we wait indefinitely until that block is available. That would be the most secure option since then users can guarantee they sync onto a chain they actually trust. |
||
.thenAccept(result::complete) | ||
.exceptionally( | ||
taskError -> { | ||
result.completeExceptionally(error); | ||
return null; | ||
}); | ||
} | ||
} else if (error != null) { | ||
LOG.error("Failed to find peers for fast sync", error); | ||
result.completeExceptionally(error); | ||
} else { | ||
result.complete(null); | ||
} | ||
return null; | ||
}); | ||
|
||
return result; | ||
} | ||
|
||
private CompletableFuture<Void> waitForAnyPeer() { | ||
LOG.warn( | ||
"Maximum wait time for fast sync reached but no peers available. Continuing to wait for any available peer."); | ||
return WaitForPeerTask.create(ethContext, ethTasksTimer).run(); | ||
} | ||
|
||
public FastSyncState selectPivotBlock() { | ||
return ethContext | ||
.getEthPeers() | ||
.bestPeer() | ||
.map( | ||
peer -> { | ||
final long pivotBlockNumber = | ||
peer.chainState().getEstimatedHeight() - syncConfig.fastSyncPivotDistance(); | ||
if (pivotBlockNumber <= BlockHeader.GENESIS_BLOCK_NUMBER) { | ||
throw new FastSyncException(CHAIN_TOO_SHORT); | ||
} else { | ||
LOG.info("Selecting block number {} as fast sync pivot block.", pivotBlockNumber); | ||
return new FastSyncState(OptionalLong.of(pivotBlockNumber)); | ||
} | ||
}) | ||
.orElseThrow(() -> new FastSyncException(NO_PEERS_AVAILABLE)); | ||
} | ||
|
||
public CompletableFuture<FastSyncState> downloadPivotBlockHeader( | ||
final FastSyncState currentState) { | ||
return new PivotBlockRetriever<>( | ||
protocolSchedule, | ||
ethContext, | ||
ethTasksTimer, | ||
currentState.getPivotBlockNumber().getAsLong()) | ||
.downloadPivotBlockHeader(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious - why prefer
==
here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Purely ease of reading and because I was confused by the
.equals
for a bit thinking it wasn't actually an enum.