|
|
|
@ -17,26 +17,26 @@ package org.hyperledger.besu.ethereum; |
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
|
import static org.mockito.ArgumentMatchers.any; |
|
|
|
|
import static org.mockito.ArgumentMatchers.anyBoolean; |
|
|
|
|
import static org.mockito.ArgumentMatchers.anyLong; |
|
|
|
|
import static org.mockito.ArgumentMatchers.eq; |
|
|
|
|
import static org.mockito.Mockito.doReturn; |
|
|
|
|
import static org.mockito.Mockito.mock; |
|
|
|
|
import static org.mockito.Mockito.spy; |
|
|
|
|
import static org.mockito.Mockito.when; |
|
|
|
|
|
|
|
|
|
import org.hyperledger.besu.datatypes.Hash; |
|
|
|
|
import org.hyperledger.besu.ethereum.chain.BadBlockManager; |
|
|
|
|
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.Block; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockDataGenerator; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockHeader; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; |
|
|
|
|
import org.hyperledger.besu.ethereum.core.MutableWorldState; |
|
|
|
|
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator; |
|
|
|
|
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator; |
|
|
|
|
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor; |
|
|
|
|
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; |
|
|
|
|
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; |
|
|
|
|
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
|
|
|
|
|
|
|
|
|
import java.util.Collections; |
|
|
|
|
import java.util.Optional; |
|
|
|
|
|
|
|
|
|
import org.junit.jupiter.api.BeforeEach; |
|
|
|
@ -44,49 +44,83 @@ import org.junit.jupiter.api.Test; |
|
|
|
|
|
|
|
|
|
public class MainnetBlockValidatorTest { |
|
|
|
|
|
|
|
|
|
private final BlockHeaderValidator blockHeaderValidator = mock(BlockHeaderValidator.class); |
|
|
|
|
private final BlockBodyValidator blockBodyValidator = mock(BlockBodyValidator.class); |
|
|
|
|
private final BlockProcessor blockProcessor = mock(BlockProcessor.class); |
|
|
|
|
private final BlockchainSetupUtil chainUtil = BlockchainSetupUtil.forMainnet(); |
|
|
|
|
private final Block block = chainUtil.getBlock(3); |
|
|
|
|
private final Block blockParent = chainUtil.getBlock(2); |
|
|
|
|
|
|
|
|
|
private final MutableBlockchain blockchain = spy(chainUtil.getBlockchain()); |
|
|
|
|
private final ProtocolContext protocolContext = mock(ProtocolContext.class); |
|
|
|
|
protected final MutableBlockchain blockchain = mock(MutableBlockchain.class); |
|
|
|
|
protected final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class); |
|
|
|
|
private final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class); |
|
|
|
|
private final MutableWorldState worldState = mock(MutableWorldState.class); |
|
|
|
|
private final BadBlockManager badBlockManager = new BadBlockManager(); |
|
|
|
|
private final BlockProcessor blockProcessor = mock(BlockProcessor.class); |
|
|
|
|
private final BlockHeaderValidator blockHeaderValidator = mock(BlockHeaderValidator.class); |
|
|
|
|
private final BlockBodyValidator blockBodyValidator = mock(BlockBodyValidator.class); |
|
|
|
|
|
|
|
|
|
private MainnetBlockValidator mainnetBlockValidator; |
|
|
|
|
private Block badBlock; |
|
|
|
|
private final MainnetBlockValidator mainnetBlockValidator = |
|
|
|
|
new MainnetBlockValidator( |
|
|
|
|
blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager); |
|
|
|
|
|
|
|
|
|
@BeforeEach |
|
|
|
|
public void setup() { |
|
|
|
|
chainUtil.importFirstBlocks(4); |
|
|
|
|
final BlockProcessingResult successfulProcessingResult = |
|
|
|
|
new BlockProcessingResult(Optional.empty(), false); |
|
|
|
|
|
|
|
|
|
when(protocolContext.getBlockchain()).thenReturn(blockchain); |
|
|
|
|
when(protocolContext.getWorldStateArchive()).thenReturn(worldStateArchive); |
|
|
|
|
mainnetBlockValidator = |
|
|
|
|
new MainnetBlockValidator( |
|
|
|
|
blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager); |
|
|
|
|
badBlock = |
|
|
|
|
new BlockDataGenerator() |
|
|
|
|
.block( |
|
|
|
|
BlockDataGenerator.BlockOptions.create() |
|
|
|
|
.setBlockNumber(2) |
|
|
|
|
.hasTransactions(false) |
|
|
|
|
.setBlockHeaderFunctions(new MainnetBlockHeaderFunctions())); |
|
|
|
|
when(worldStateArchive.getMutable(any(BlockHeader.class), anyBoolean())) |
|
|
|
|
.thenReturn(Optional.of(worldState)); |
|
|
|
|
when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class))) |
|
|
|
|
.thenReturn(Optional.of(worldState)); |
|
|
|
|
when(worldStateArchive.getMutable()).thenReturn(worldState); |
|
|
|
|
when(blockHeaderValidator.validateHeader(any(), any(), any())).thenReturn(true); |
|
|
|
|
when(blockHeaderValidator.validateHeader(any(), any(), any(), any())).thenReturn(true); |
|
|
|
|
when(blockBodyValidator.validateBody(any(), any(), any(), any(), any())).thenReturn(true); |
|
|
|
|
when(blockBodyValidator.validateBodyLight(any(), any(), any(), any())).thenReturn(true); |
|
|
|
|
when(blockProcessor.processBlock(any(), any(), any())).thenReturn(successfulProcessingResult); |
|
|
|
|
when(blockProcessor.processBlock(any(), any(), any(), any())) |
|
|
|
|
.thenReturn(successfulProcessingResult); |
|
|
|
|
when(blockProcessor.processBlock(any(), any(), any(), any(), any())) |
|
|
|
|
.thenReturn(successfulProcessingResult); |
|
|
|
|
when(blockProcessor.processBlock(any(), any(), any(), any(), any(), any(), any(), any())) |
|
|
|
|
.thenReturn(successfulProcessingResult); |
|
|
|
|
|
|
|
|
|
assertNoBadBlocks(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldDetectAndCacheInvalidBlocksWhenParentBlockNotPresent() { |
|
|
|
|
when(blockchain.getBlockHeader(anyLong())).thenReturn(Optional.empty()); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(1); |
|
|
|
|
public void validateAndProcessBlock_onSuccess() { |
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
|
|
|
|
|
assertThat(result.isSuccessful()).isTrue(); |
|
|
|
|
assertNoBadBlocks(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldDetectAndCacheInvalidBlocksWhenHeaderInvalid() { |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))) |
|
|
|
|
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader())); |
|
|
|
|
public void validateAndProcessBlock_whenParentBlockNotPresent() { |
|
|
|
|
final Hash parentHash = blockParent.getHash(); |
|
|
|
|
doReturn(Optional.empty()).when(blockchain).getBlockHeader(eq(parentHash)); |
|
|
|
|
|
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
|
|
|
|
|
final String expectedError = "Parent block with hash " + parentHash + " not present"; |
|
|
|
|
assertValidationFailed(result, expectedError); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void validateAndProcessBlock_whenHeaderInvalid() { |
|
|
|
|
when(blockHeaderValidator.validateHeader( |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
any(BlockHeader.class), |
|
|
|
@ -94,177 +128,181 @@ public class MainnetBlockValidatorTest { |
|
|
|
|
eq(HeaderValidationMode.DETACHED_ONLY))) |
|
|
|
|
.thenReturn(false); |
|
|
|
|
|
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(1); |
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
|
|
|
|
|
final String expectedError = "Header validation failed (DETACHED_ONLY)"; |
|
|
|
|
assertValidationFailed(result, expectedError); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldDetectAndCacheInvalidBlocksWhenParentWorldStateNotAvailable() { |
|
|
|
|
public void validateAndProcessBlock_whenBlockBodyInvalid() { |
|
|
|
|
when(blockBodyValidator.validateBody(any(), eq(block), any(), any(), any())).thenReturn(false); |
|
|
|
|
|
|
|
|
|
final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader(); |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))).thenReturn(Optional.of(blockHeader)); |
|
|
|
|
when(blockHeaderValidator.validateHeader( |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
eq(protocolContext), |
|
|
|
|
eq(HeaderValidationMode.DETACHED_ONLY))) |
|
|
|
|
.thenReturn(true); |
|
|
|
|
when(worldStateArchive.getMutable(eq(blockHeader), anyBoolean())).thenReturn(Optional.empty()); |
|
|
|
|
|
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(1); |
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
|
|
|
|
|
final String expectedError = "failed to validate output of imported block"; |
|
|
|
|
assertValidationFailed(result, expectedError); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldDetectAndCacheInvalidBlocksWhenProcessBlockFailed() { |
|
|
|
|
final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader(); |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))).thenReturn(Optional.of(blockHeader)); |
|
|
|
|
when(blockHeaderValidator.validateHeader( |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
eq(protocolContext), |
|
|
|
|
eq(HeaderValidationMode.DETACHED_ONLY))) |
|
|
|
|
.thenReturn(true); |
|
|
|
|
when(worldStateArchive.getMutable(eq(blockHeader), anyBoolean())) |
|
|
|
|
.thenReturn(Optional.of(mock(MutableWorldState.class))); |
|
|
|
|
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(badBlock))) |
|
|
|
|
.thenReturn(new BlockProcessingResult(Optional.empty())); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(1); |
|
|
|
|
public void validateAndProcessBlock_whenParentWorldStateNotAvailable() { |
|
|
|
|
when(worldStateArchive.getMutable(eq(blockParent.getHeader()), anyBoolean())) |
|
|
|
|
.thenReturn(Optional.empty()); |
|
|
|
|
|
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
|
|
|
|
|
final String expectedError = |
|
|
|
|
"Unable to process block because parent world state " |
|
|
|
|
+ blockParent.getHeader().getStateRoot() |
|
|
|
|
+ " is not available"; |
|
|
|
|
assertValidationFailed(result, expectedError); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldDetectAndCacheInvalidBlocksWhenBodyInvalid() { |
|
|
|
|
final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader(); |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))).thenReturn(Optional.of(blockHeader)); |
|
|
|
|
when(blockHeaderValidator.validateHeader( |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
eq(protocolContext), |
|
|
|
|
eq(HeaderValidationMode.DETACHED_ONLY))) |
|
|
|
|
.thenReturn(true); |
|
|
|
|
when(worldStateArchive.getMutable(eq(blockHeader), anyBoolean())) |
|
|
|
|
.thenReturn(Optional.of(mock(MutableWorldState.class))); |
|
|
|
|
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(badBlock))) |
|
|
|
|
.thenReturn(new BlockProcessingResult(Optional.empty())); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(1); |
|
|
|
|
public void validateAndProcessBlock_whenProcessBlockFails() { |
|
|
|
|
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block))) |
|
|
|
|
.thenReturn(BlockProcessingResult.FAILED); |
|
|
|
|
|
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
|
|
|
|
|
final String expectedError = "processing failed"; |
|
|
|
|
assertValidationFailed(result, expectedError); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldNotCacheWhenValidBlocks() { |
|
|
|
|
public void validateAndProcessBlock_withShouldRecordBadBlockFalse() { |
|
|
|
|
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block))) |
|
|
|
|
.thenReturn(BlockProcessingResult.FAILED); |
|
|
|
|
|
|
|
|
|
MutableWorldState mockWorldState = mock(MutableWorldState.class); |
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
false, |
|
|
|
|
false); |
|
|
|
|
|
|
|
|
|
final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader(); |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))).thenReturn(Optional.of(blockHeader)); |
|
|
|
|
when(blockHeaderValidator.validateHeader( |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
eq(protocolContext), |
|
|
|
|
eq(HeaderValidationMode.DETACHED_ONLY))) |
|
|
|
|
.thenReturn(true); |
|
|
|
|
when(worldStateArchive.getMutable(eq(blockHeader), anyBoolean())) |
|
|
|
|
.thenReturn(Optional.of(mockWorldState)); |
|
|
|
|
when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class))) |
|
|
|
|
.thenReturn(Optional.of(mockWorldState)); |
|
|
|
|
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(badBlock))) |
|
|
|
|
.thenReturn(new BlockProcessingResult(Optional.empty())); |
|
|
|
|
when(blockBodyValidator.validateBody( |
|
|
|
|
eq(protocolContext), |
|
|
|
|
eq(badBlock), |
|
|
|
|
any(), |
|
|
|
|
any(), |
|
|
|
|
eq(HeaderValidationMode.DETACHED_ONLY))) |
|
|
|
|
.thenReturn(true); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
assertThat(badBlockManager.getBadBlocks()).isEmpty(); |
|
|
|
|
assertThat(result.isFailed()).isTrue(); |
|
|
|
|
assertNoBadBlocks(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void shouldReturnBadBlockBasedOnTheHash() { |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))) |
|
|
|
|
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader())); |
|
|
|
|
when(blockHeaderValidator.validateHeader( |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
any(BlockHeader.class), |
|
|
|
|
eq(protocolContext), |
|
|
|
|
eq(HeaderValidationMode.DETACHED_ONLY))) |
|
|
|
|
.thenReturn(false); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY); |
|
|
|
|
assertThat(badBlockManager.getBadBlock(badBlock.getHash())).containsSame(badBlock); |
|
|
|
|
public void validateAndProcessBlock_withShouldRecordBadBlockTrue() { |
|
|
|
|
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block))) |
|
|
|
|
.thenReturn(BlockProcessingResult.FAILED); |
|
|
|
|
|
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
false, |
|
|
|
|
true); |
|
|
|
|
|
|
|
|
|
assertThat(result.isFailed()).isTrue(); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void when_shouldRecordBadBlockIsFalse_Expect_BlockNotAddedToBadBlockManager() { |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))) |
|
|
|
|
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader())); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
false, |
|
|
|
|
false); |
|
|
|
|
|
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0); |
|
|
|
|
public void validateAndProcessBlock_withShouldRecordBadBlockNotSet() { |
|
|
|
|
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(block))) |
|
|
|
|
.thenReturn(BlockProcessingResult.FAILED); |
|
|
|
|
|
|
|
|
|
BlockProcessingResult result = |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
false); |
|
|
|
|
|
|
|
|
|
assertThat(result.isFailed()).isTrue(); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void when_shouldRecordBadBlockIsTrue_Expect_BlockAddedToBadBlockManager() { |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))) |
|
|
|
|
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader())); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
false, |
|
|
|
|
true); |
|
|
|
|
|
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(1); |
|
|
|
|
public void fastBlockValidation_onSuccess() { |
|
|
|
|
final boolean isValid = |
|
|
|
|
mainnetBlockValidator.fastBlockValidation( |
|
|
|
|
protocolContext, |
|
|
|
|
block, |
|
|
|
|
Collections.emptyList(), |
|
|
|
|
HeaderValidationMode.FULL, |
|
|
|
|
HeaderValidationMode.FULL); |
|
|
|
|
|
|
|
|
|
assertThat(isValid).isTrue(); |
|
|
|
|
assertNoBadBlocks(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void when_shouldRecordBadBlockIsNotSet_Expect_BlockAddedToBadBlockManager() { |
|
|
|
|
when(blockchain.getBlockHeader(any(Hash.class))) |
|
|
|
|
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader())); |
|
|
|
|
mainnetBlockValidator.validateAndProcessBlock( |
|
|
|
|
protocolContext, |
|
|
|
|
badBlock, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
HeaderValidationMode.DETACHED_ONLY, |
|
|
|
|
false); |
|
|
|
|
|
|
|
|
|
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(1); |
|
|
|
|
public void fastBlockValidation_onFailedHeaderValidation() { |
|
|
|
|
final HeaderValidationMode validationMode = HeaderValidationMode.FULL; |
|
|
|
|
when(blockHeaderValidator.validateHeader( |
|
|
|
|
any(BlockHeader.class), eq(protocolContext), eq(validationMode))) |
|
|
|
|
.thenReturn(false); |
|
|
|
|
|
|
|
|
|
final boolean isValid = |
|
|
|
|
mainnetBlockValidator.fastBlockValidation( |
|
|
|
|
protocolContext, block, Collections.emptyList(), validationMode, validationMode); |
|
|
|
|
|
|
|
|
|
assertThat(isValid).isFalse(); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void fastBlockValidation_onFailedBodyValidation() { |
|
|
|
|
final HeaderValidationMode validationMode = HeaderValidationMode.FULL; |
|
|
|
|
when(blockBodyValidator.validateBodyLight( |
|
|
|
|
eq(protocolContext), eq(block), any(), eq(validationMode))) |
|
|
|
|
.thenReturn(false); |
|
|
|
|
|
|
|
|
|
final boolean isValid = |
|
|
|
|
mainnetBlockValidator.fastBlockValidation( |
|
|
|
|
protocolContext, block, Collections.emptyList(), validationMode, validationMode); |
|
|
|
|
|
|
|
|
|
assertThat(isValid).isFalse(); |
|
|
|
|
assertBadBlockIsTracked(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void assertNoBadBlocks() { |
|
|
|
|
assertThat(badBlockManager.getBadBlocks()).isEmpty(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void assertBadBlockIsTracked(final Block badBlock) { |
|
|
|
|
assertThat(badBlockManager.getBadBlocks()).containsExactly(badBlock); |
|
|
|
|
assertThat(badBlockManager.getBadBlock(badBlock.getHash())).contains(block); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void assertValidationFailed( |
|
|
|
|
final BlockProcessingResult result, final String expectedError) { |
|
|
|
|
assertThat(result.isFailed()).isTrue(); |
|
|
|
|
assertThat(result.errorMessage).isPresent(); |
|
|
|
|
assertThat(result.errorMessage.get()).containsIgnoringWhitespaces(expectedError); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|