mirror of https://github.com/hyperledger/besu
Switch to new sync target if it exceeds the td threshold (#1286)
Increase total difficulty threshold required to switch sync targets. Increase threshold for changing sync target based on height to 200. Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
9fb04b44b7
commit
cfaa83747a
@ -0,0 +1,63 @@ |
||||
/* |
||||
* 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.fullsync; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.eth.manager.ChainState; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthPeer; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthPeers; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; |
||||
import tech.pegasys.pantheon.util.uint.UInt256; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
public class BetterSyncTargetEvaluator { |
||||
|
||||
private final SynchronizerConfiguration config; |
||||
private final EthPeers ethPeers; |
||||
|
||||
public BetterSyncTargetEvaluator( |
||||
final SynchronizerConfiguration config, final EthPeers ethPeers) { |
||||
this.config = config; |
||||
this.ethPeers = ethPeers; |
||||
} |
||||
|
||||
public boolean shouldSwitchSyncTarget(final EthPeer currentSyncTarget) { |
||||
final ChainState currentPeerChainState = currentSyncTarget.chainState(); |
||||
final Optional<EthPeer> maybeBestPeer = ethPeers.bestPeer(); |
||||
|
||||
return maybeBestPeer |
||||
.map( |
||||
bestPeer -> { |
||||
if (EthPeers.BEST_CHAIN.compare(bestPeer, currentSyncTarget) <= 0) { |
||||
// Our current target is better or equal to the best peer
|
||||
return false; |
||||
} |
||||
// Require some threshold to be exceeded before switching targets to keep some
|
||||
// stability when multiple peers are in range of each other
|
||||
final ChainState bestPeerChainState = bestPeer.chainState(); |
||||
final UInt256 tdDifference = |
||||
bestPeerChainState |
||||
.getBestBlock() |
||||
.getTotalDifficulty() |
||||
.minus(currentPeerChainState.getBestBlock().getTotalDifficulty()); |
||||
if (tdDifference.compareTo(config.downloaderChangeTargetThresholdByTd()) > 0) { |
||||
return true; |
||||
} |
||||
final long heightDifference = |
||||
bestPeerChainState.getEstimatedHeight() |
||||
- currentPeerChainState.getEstimatedHeight(); |
||||
return heightDifference > config.downloaderChangeTargetThresholdByHeight(); |
||||
}) |
||||
.orElse(false); |
||||
} |
||||
} |
@ -0,0 +1,157 @@ |
||||
/* |
||||
* 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.fullsync; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.Hash; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.ChainState; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthPeer; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthPeers; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; |
||||
import tech.pegasys.pantheon.util.uint.UInt256; |
||||
|
||||
import java.util.Optional; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
public class BetterSyncTargetEvaluatorTest { |
||||
|
||||
private static final int CURRENT_TARGET_HEIGHT = 10; |
||||
private static final int CURRENT_TARGET_TD = 50; |
||||
private static final int HEIGHT_THRESHOLD = 100; |
||||
private static final int TD_THRESHOLD = 5; |
||||
private final EthPeers ethPeers = mock(EthPeers.class); |
||||
private final EthPeer currentTarget = peer(CURRENT_TARGET_HEIGHT, CURRENT_TARGET_TD); |
||||
private final BetterSyncTargetEvaluator evaluator = |
||||
new BetterSyncTargetEvaluator( |
||||
SynchronizerConfiguration.builder() |
||||
.downloaderChangeTargetThresholdByHeight(HEIGHT_THRESHOLD) |
||||
.downloaderChangeTargetThresholdByTd(UInt256.of(TD_THRESHOLD)) |
||||
.build(), |
||||
ethPeers); |
||||
|
||||
@Test |
||||
public void shouldNotSwitchTargetsIfNoBestPeerIsAvailable() { |
||||
when(ethPeers.bestPeer()).thenReturn(Optional.empty()); |
||||
|
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotSwitchTargetWhenBestPeerHasLowerHeightAndDifficulty() { |
||||
bestPeerWithDelta(-1, -1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotSwitchTargetWhenBestPeerHasSameHeightAndLowerDifficulty() { |
||||
bestPeerWithDelta(0, -1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotSwitchTargetWhenBestPeerHasLowerHeightAndSameDifficulty() { |
||||
bestPeerWithDelta(-1, 0); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotSwitchTargetWhenBestPeerHasGreaterHeightAndLowerDifficulty() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD + 1, -1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotSwitchTargetWhenBestPeerHasEqualHeightAndDifficulty() { |
||||
bestPeerWithDelta(0, 0); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotSwitchWhenHeightAndTdHigherWithinThreshold() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD - 1, TD_THRESHOLD - 1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotSwitchWhenHeightAndTdHigherEqualToThreshold() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD, TD_THRESHOLD); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldSwitchWhenHeightExceedsThresholdAndDifficultyEqual() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD + 1, 0); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldSwitchWhenHeightExceedsThresholdAndDifficultyWithinThreshold() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD + 1, TD_THRESHOLD - 1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldSwitchWhenHeightAndDifficultyExceedThreshold() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD + 1, TD_THRESHOLD + 1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotSwitchWhenHeightExceedsThresholdButDifficultyIsLower() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD + 1, -1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldSwitchWhenDifficultyExceedsThresholdAndHeightIsEqual() { |
||||
bestPeerWithDelta(0, TD_THRESHOLD + 1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldSwitchWhenDifficultyExceedsThresholdAndHeightIsLower() { |
||||
bestPeerWithDelta(-1, TD_THRESHOLD + 1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldSwitchWhenDifficultyExceedsThresholdAndHeightIsWithinThreshold() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD - 1, TD_THRESHOLD + 1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldSwitchWhenHeightAndDifficultyExceedsThreshold() { |
||||
bestPeerWithDelta(HEIGHT_THRESHOLD + 1, TD_THRESHOLD + 1); |
||||
assertThat(evaluator.shouldSwitchSyncTarget(currentTarget)).isTrue(); |
||||
} |
||||
|
||||
private void bestPeerWithDelta(final long height, final long totalDifficulty) { |
||||
final EthPeer bestPeer = |
||||
peer(CURRENT_TARGET_HEIGHT + height, CURRENT_TARGET_TD + totalDifficulty); |
||||
when(ethPeers.bestPeer()).thenReturn(Optional.of(bestPeer)); |
||||
} |
||||
|
||||
private EthPeer peer(final long chainHeight, final long totalDifficulty) { |
||||
final EthPeer peer = mock(EthPeer.class); |
||||
final ChainState chainState = new ChainState(); |
||||
chainState.updateHeightEstimate(chainHeight); |
||||
chainState.statusReceived(Hash.EMPTY, UInt256.of(totalDifficulty)); |
||||
when(peer.chainState()).thenReturn(chainState); |
||||
return peer; |
||||
} |
||||
} |
Loading…
Reference in new issue