mirror of https://github.com/hyperledger/besu
[NC-1384] Disable mining while catching up to chain head (#125)
parent
ba18fcf163
commit
d2583e3ef2
@ -0,0 +1,27 @@ |
|||||||
|
apply plugin: 'java-library' |
||||||
|
|
||||||
|
jar { |
||||||
|
baseName 'pantheon-blockcreation' |
||||||
|
manifest { |
||||||
|
attributes('Implementation-Title': baseName, |
||||||
|
'Implementation-Version': project.version) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
implementation project(':ethereum:core') |
||||||
|
implementation project(':ethereum:eth') |
||||||
|
implementation project(':util') |
||||||
|
implementation project(':crypto') |
||||||
|
implementation project(':services:kvstore') |
||||||
|
|
||||||
|
implementation 'io.vertx:vertx-core' |
||||||
|
implementation 'com.google.guava:guava' |
||||||
|
|
||||||
|
testImplementation 'junit:junit' |
||||||
|
testImplementation 'org.assertj:assertj-core' |
||||||
|
testImplementation 'org.awaitility:awaitility' |
||||||
|
testImplementation 'org.mockito:mockito-core' |
||||||
|
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts') |
||||||
|
testImplementation project(path: ':ethereum:core', configuration: 'testArtifacts') |
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2018 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.blockcreation; |
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
import static org.mockito.Mockito.times; |
||||||
|
import static org.mockito.Mockito.verify; |
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||||
|
import static org.mockito.Mockito.verifyZeroInteractions; |
||||||
|
import static org.mockito.Mockito.when; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.ethereum.chain.BlockAddedEvent; |
||||||
|
import tech.pegasys.pantheon.ethereum.chain.Blockchain; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Block; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockBody; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture; |
||||||
|
import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
|
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
public class AbstractMiningCoordinatorTest { |
||||||
|
|
||||||
|
private static final Block BLOCK = |
||||||
|
new Block( |
||||||
|
new BlockHeaderTestFixture().buildHeader(), |
||||||
|
new BlockBody(Collections.emptyList(), Collections.emptyList())); |
||||||
|
private final Blockchain blockchain = mock(Blockchain.class); |
||||||
|
private final EthHashMinerExecutor minerExecutor = mock(EthHashMinerExecutor.class); |
||||||
|
private final SyncState syncState = mock(SyncState.class); |
||||||
|
private final EthHashBlockMiner blockMiner = mock(EthHashBlockMiner.class); |
||||||
|
private final TestMiningCoordinator miningCoordinator = |
||||||
|
new TestMiningCoordinator(blockchain, minerExecutor, syncState); |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() { |
||||||
|
when(minerExecutor.startAsyncMining(any(), any())).thenReturn(blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldNotStartMiningWhenEnabledAndOutOfSync() { |
||||||
|
when(syncState.isInSync()).thenReturn(false); |
||||||
|
miningCoordinator.enable(); |
||||||
|
verifyZeroInteractions(minerExecutor, blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldStartMiningWhenEnabledAndInSync() { |
||||||
|
when(syncState.isInSync()).thenReturn(true); |
||||||
|
miningCoordinator.enable(); |
||||||
|
verify(minerExecutor).startAsyncMining(any(), any()); |
||||||
|
verifyNoMoreInteractions(minerExecutor, blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldStartMiningWhenEnabledAndBecomeInSync() { |
||||||
|
when(syncState.isInSync()).thenReturn(false); |
||||||
|
miningCoordinator.enable(); |
||||||
|
|
||||||
|
miningCoordinator.inSyncChanged(true); |
||||||
|
|
||||||
|
verify(minerExecutor).startAsyncMining(any(), any()); |
||||||
|
verifyNoMoreInteractions(minerExecutor, blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldHaltMiningWhenBecomingOutOfSync() { |
||||||
|
when(syncState.isInSync()).thenReturn(true); |
||||||
|
miningCoordinator.enable(); |
||||||
|
verify(minerExecutor).startAsyncMining(any(), any()); |
||||||
|
|
||||||
|
miningCoordinator.inSyncChanged(false); |
||||||
|
|
||||||
|
verify(blockMiner).cancel(); |
||||||
|
verifyNoMoreInteractions(minerExecutor, blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldNotStartWhenBlockAddedAndOutOfSync() { |
||||||
|
when(syncState.isInSync()).thenReturn(false); |
||||||
|
miningCoordinator.enable(); |
||||||
|
|
||||||
|
miningCoordinator.onBlockAdded(BlockAddedEvent.createForHeadAdvancement(BLOCK), blockchain); |
||||||
|
|
||||||
|
verifyNoMoreInteractions(minerExecutor, blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldRestartMiningWhenBlockAddedAndInSync() { |
||||||
|
when(syncState.isInSync()).thenReturn(true); |
||||||
|
miningCoordinator.enable(); |
||||||
|
|
||||||
|
miningCoordinator.onBlockAdded(BlockAddedEvent.createForHeadAdvancement(BLOCK), blockchain); |
||||||
|
|
||||||
|
verify(blockMiner).cancel(); |
||||||
|
verify(minerExecutor, times(2)).startAsyncMining(any(), any()); |
||||||
|
|
||||||
|
verifyNoMoreInteractions(minerExecutor, blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldNotStartMiningWhenBecomingInSyncIfMinerNotEnabled() { |
||||||
|
when(syncState.isInSync()).thenReturn(true); |
||||||
|
miningCoordinator.inSyncChanged(true); |
||||||
|
verifyNoMoreInteractions(minerExecutor, blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldNotStartMiningWhenBlockAddedAndInSyncIfMinerNotEnabled() { |
||||||
|
when(syncState.isInSync()).thenReturn(true); |
||||||
|
miningCoordinator.onBlockAdded(BlockAddedEvent.createForHeadAdvancement(BLOCK), blockchain); |
||||||
|
verifyNoMoreInteractions(minerExecutor, blockMiner); |
||||||
|
} |
||||||
|
|
||||||
|
public static class TestMiningCoordinator |
||||||
|
extends AbstractMiningCoordinator<Void, EthHashBlockMiner> { |
||||||
|
|
||||||
|
public TestMiningCoordinator( |
||||||
|
final Blockchain blockchain, |
||||||
|
final AbstractMinerExecutor<Void, EthHashBlockMiner> executor, |
||||||
|
final SyncState syncState) { |
||||||
|
super(blockchain, executor, syncState); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2018 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.chain; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.ethereum.core.Block; |
||||||
|
|
||||||
|
public interface MinedBlockObserver { |
||||||
|
void blockMined(Block block); |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2018 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.state; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
import static org.mockito.Mockito.verify; |
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||||
|
import static org.mockito.Mockito.when; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.ethereum.chain.BlockAddedEvent; |
||||||
|
import tech.pegasys.pantheon.ethereum.chain.BlockAddedObserver; |
||||||
|
import tech.pegasys.pantheon.ethereum.chain.Blockchain; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Block; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockBody; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture; |
||||||
|
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 java.util.Collections; |
||||||
|
|
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.mockito.ArgumentCaptor; |
||||||
|
|
||||||
|
public class SyncStateTest { |
||||||
|
|
||||||
|
private static final long OUR_CHAIN_HEAD_NUMBER = 500; |
||||||
|
private static final long TARGET_SYNC_NUMBER = OUR_CHAIN_HEAD_NUMBER + 100; |
||||||
|
private final Blockchain blockchain = mock(Blockchain.class); |
||||||
|
private final EthPeers ethPeers = mock(EthPeers.class); |
||||||
|
private final SyncState.InSyncListener inSyncListener = mock(SyncState.InSyncListener.class); |
||||||
|
private final EthPeer peer = mock(EthPeer.class); |
||||||
|
private final ChainState peerChainHead = new ChainState(); |
||||||
|
private SyncState syncState; |
||||||
|
private BlockAddedObserver blockAddedObserver; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() { |
||||||
|
final ArgumentCaptor<BlockAddedObserver> captor = |
||||||
|
ArgumentCaptor.forClass(BlockAddedObserver.class); |
||||||
|
when(blockchain.observeBlockAdded(captor.capture())).thenReturn(1L); |
||||||
|
when(peer.chainState()).thenReturn(peerChainHead); |
||||||
|
when(blockchain.getChainHeadBlockNumber()).thenReturn(OUR_CHAIN_HEAD_NUMBER); |
||||||
|
syncState = new SyncState(blockchain, ethPeers); |
||||||
|
blockAddedObserver = captor.getValue(); |
||||||
|
syncState.addInSyncListener(inSyncListener); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldBeInSyncWhenNoSyncTargetHasBeenSet() { |
||||||
|
assertThat(syncState.isInSync()).isTrue(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldSwitchToNotInSyncWhenSyncTargetWithBetterChainSet() { |
||||||
|
final BlockHeader bestBlockHeader = targetBlockHeader(); |
||||||
|
peerChainHead.update(bestBlockHeader); |
||||||
|
syncState.setSyncTarget(peer, bestBlockHeader); |
||||||
|
assertThat(syncState.isInSync()).isFalse(); |
||||||
|
verify(inSyncListener).onSyncStatusChanged(false); |
||||||
|
verifyNoMoreInteractions(inSyncListener); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldSwitchToInSyncWhenSyncTargetCleared() { |
||||||
|
setupOutOfSyncState(); |
||||||
|
|
||||||
|
syncState.clearSyncTarget(); |
||||||
|
|
||||||
|
verify(inSyncListener).onSyncStatusChanged(true); |
||||||
|
verifyNoMoreInteractions(inSyncListener); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldBecomeInSyncWhenOurBlockchainCatchesUp() { |
||||||
|
setupOutOfSyncState(); |
||||||
|
|
||||||
|
when(blockchain.getChainHeadBlockNumber()).thenReturn(TARGET_SYNC_NUMBER); |
||||||
|
blockAddedObserver.onBlockAdded( |
||||||
|
BlockAddedEvent.createForHeadAdvancement( |
||||||
|
new Block( |
||||||
|
targetBlockHeader(), |
||||||
|
new BlockBody(Collections.emptyList(), Collections.emptyList()))), |
||||||
|
blockchain); |
||||||
|
|
||||||
|
assertThat(syncState.isInSync()).isTrue(); |
||||||
|
verify(inSyncListener).onSyncStatusChanged(true); |
||||||
|
} |
||||||
|
|
||||||
|
private void setupOutOfSyncState() { |
||||||
|
final BlockHeader bestBlockHeader = targetBlockHeader(); |
||||||
|
peerChainHead.update(bestBlockHeader); |
||||||
|
syncState.setSyncTarget(peer, bestBlockHeader); |
||||||
|
assertThat(syncState.isInSync()).isFalse(); |
||||||
|
verify(inSyncListener).onSyncStatusChanged(false); |
||||||
|
} |
||||||
|
|
||||||
|
private BlockHeader targetBlockHeader() { |
||||||
|
return new BlockHeaderTestFixture().number(TARGET_SYNC_NUMBER).buildHeader(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue