@Test public void locallyGeneratedBlockInvalidatesMiningEvenIfInTurn() { // Note also - validators is an hard-ordered LIST, thus in-turn will follow said list - block_1 // should be created by Proposer, and thus will be in-turn. final CliqueMiningCoordinator coordinator = new CliqueMiningCoordinator(blockChain, minerExecutor, syncState, miningTracker); coordinator.enable(); verify(minerExecutor, times(1)).startAsyncMining(any(), any()); reset(minerExecutor); when(minerExecutor.startAsyncMining(any(), any())).thenReturn(blockMiner); final Block importedBlock = createEmptyBlock(1, blockChain.getChainHeadHash(), proposerKeys); blockChain.appendBlock(importedBlock, Lists.emptyList()); // The minerExecutor should not be invoked as the mining operation was conducted by an in-turn // validator, and the created block came from an out-turn validator. ArgumentCaptor<BlockHeader> varArgs = ArgumentCaptor.forClass(BlockHeader.class); verify(minerExecutor, times(1)).startAsyncMining(any(), varArgs.capture()); assertThat(varArgs.getValue()).isEqualTo(blockChain.getChainHeadHeader()); }
@Test public void outOfTurnBlockImportedAtHigherLevelInterruptsMiningOperation() { // As the head of the blockChain is 1 (which effectively doesn't have a signer, all validators // are able to propose. when(blockMiner.getParentHeader()).thenReturn(blockChain.getChainHeadHeader()); // Note also - validators is an hard-ordered LIST, thus in-turn will follow said list - block_1 // should be created by proposer. final CliqueMiningCoordinator coordinator = new CliqueMiningCoordinator(blockChain, minerExecutor, syncState, miningTracker); coordinator.enable(); verify(minerExecutor, times(1)).startAsyncMining(any(), any()); reset(minerExecutor); when(minerExecutor.startAsyncMining(any(), any())).thenReturn(blockMiner); final Block importedBlock = createEmptyBlock(2, blockChain.getChainHeadHash(), validatorKeys); blockChain.appendBlock(importedBlock, Lists.emptyList()); // The minerExecutor should not be invoked as the mining operation was conducted by an in-turn // validator, and the created block came from an out-turn validator. ArgumentCaptor<BlockHeader> varArgs = ArgumentCaptor.forClass(BlockHeader.class); verify(minerExecutor, times(1)).startAsyncMining(any(), varArgs.capture()); assertThat(varArgs.getValue()).isEqualTo(blockChain.getChainHeadHeader()); }
private BlockchainWithData setupBlockchain( final int blocksToAdd, final List<Address> accountsToSetup, final List<UInt256> storageKeys) { checkArgument(blocksToAdd >= 1, "Must add at least one block to the queries"); final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive(); // Generate some queries data final List<BlockData> blockData = new ArrayList<>(blocksToAdd); final List<Block> blocks = gen.blockSequence(blocksToAdd, worldStateArchive, accountsToSetup, storageKeys); for (int i = 0; i < blocksToAdd; i++) { final Block block = blocks.get(i); final List<TransactionReceipt> receipts = gen.receipts(block); blockData.add(new BlockData(block, receipts)); } // Setup blockchain final MutableBlockchain blockchain = createInMemoryBlockchain(blocks.get(0)); blockData .subList(1, blockData.size()) .forEach(b -> blockchain.appendBlock(b.block, b.receipts)); return new BlockchainWithData(blockchain, blockData, worldStateArchive); }
@Test public void outOfTurnBlockImportedInterruptsOutOfTurnMiningOperation() { blockChain.appendBlock( createEmptyBlock(1, blockChain.getChainHeadHash(), validatorKeys), Lists.emptyList()); when(blockMiner.getParentHeader()).thenReturn(blockChain.getChainHeadHeader()); // Note also - validators is an hard-ordered LIST, thus in-turn will follow said list - block_2 // should be created by 'validator', thus Proposer is out-of-turn. final CliqueMiningCoordinator coordinator = new CliqueMiningCoordinator(blockChain, minerExecutor, syncState, miningTracker); coordinator.enable(); verify(minerExecutor, times(1)).startAsyncMining(any(), any()); reset(minerExecutor); when(minerExecutor.startAsyncMining(any(), any())).thenReturn(blockMiner); final Block importedBlock = createEmptyBlock(2, blockChain.getChainHeadHash(), validatorKeys); blockChain.appendBlock(importedBlock, Lists.emptyList()); // The minerExecutor should not be invoked as the mining operation was conducted by an in-turn // validator, and the created block came from an out-turn validator. ArgumentCaptor<BlockHeader> varArgs = ArgumentCaptor.forClass(BlockHeader.class); verify(minerExecutor, times(1)).startAsyncMining(any(), varArgs.capture()); assertThat(varArgs.getValue()).isEqualTo(blockChain.getChainHeadHeader()); }
@Test public void outOfTurnBlockImportedInterruptsNonRunningMiner() { blockChain.appendBlock( createEmptyBlock(1, blockChain.getChainHeadHash(), proposerKeys), Lists.emptyList()); when(blockMiner.getParentHeader()).thenReturn(blockChain.getChainHeadHeader()); // Note also - validators is an hard-ordered LIST, thus in-turn will follow said list - block_2 // should be created by 'validator', thus Proposer is out-of-turn. final CliqueMiningCoordinator coordinator = new CliqueMiningCoordinator(blockChain, minerExecutor, syncState, miningTracker); coordinator.enable(); verify(minerExecutor, times(1)).startAsyncMining(any(), any()); reset(minerExecutor); when(minerExecutor.startAsyncMining(any(), any())).thenReturn(blockMiner); final Block importedBlock = createEmptyBlock(2, blockChain.getChainHeadHash(), validatorKeys); blockChain.appendBlock(importedBlock, Lists.emptyList()); // The minerExecutor should not be invoked as the mining operation was conducted by an in-turn // validator, and the created block came from an out-turn validator. ArgumentCaptor<BlockHeader> varArgs = ArgumentCaptor.forClass(BlockHeader.class); verify(minerExecutor, times(1)).startAsyncMining(any(), varArgs.capture()); assertThat(varArgs.getValue()).isEqualTo(blockChain.getChainHeadHeader()); }
@Test public void outOfTurnBlockImportedDoesNotInterruptInTurnMiningOperation() { // As the head of the blockChain is 0 (which effectively doesn't have a signer, all validators // are able to propose. when(blockMiner.getParentHeader()).thenReturn(blockChain.getChainHeadHeader()); // Note also - validators is an hard-ordered LIST, thus in-turn will follow said list - block_1 // should be created by proposer. final CliqueMiningCoordinator coordinator = new CliqueMiningCoordinator(blockChain, minerExecutor, syncState, miningTracker); coordinator.enable(); verify(minerExecutor, times(1)).startAsyncMining(any(), any()); reset(minerExecutor); final Block importedBlock = createEmptyBlock(1, blockChain.getChainHeadHash(), validatorKeys); blockChain.appendBlock(importedBlock, Lists.emptyList()); // The minerExecutor should not be invoked as the mining operation was conducted by an in-turn // validator, and the created block came from an out-turn validator. verify(minerExecutor, never()).startAsyncMining(any(), any()); }
private Block appendBlock( final UInt256 difficulty, final BlockHeader parentBlock, final Transaction... transactionsToAdd) { final List<Transaction> transactionList = asList(transactionsToAdd); final Block block = new Block( new BlockHeaderTestFixture() .difficulty(difficulty) .parentHash(parentBlock.getHash()) .number(parentBlock.getNumber() + 1) .buildHeader(), new BlockBody(transactionList, emptyList())); final List<TransactionReceipt> transactionReceipts = transactionList .stream() .map(transaction -> new TransactionReceipt(1, 1, emptyList())) .collect(toList()); blockchain.appendBlock(block, transactionReceipts); return block; }
private MutableBlockchain createShortChain(final long lastBlockToInclude) { final BlockHeader genesisHeader = blockchain.getBlockHeader(BlockHeader.GENESIS_BLOCK_NUMBER).get(); final BlockBody genesisBody = blockchain.getBlockBody(genesisHeader.getHash()).get(); final Block genesisBlock = new Block(genesisHeader, genesisBody); final MutableBlockchain shortChain = createInMemoryBlockchain(genesisBlock); long nextBlock = genesisHeader.getNumber() + 1; while (nextBlock <= lastBlockToInclude) { final BlockHeader header = blockchain.getBlockHeader(nextBlock).get(); final BlockBody body = blockchain.getBlockBody(header.getHash()).get(); final List<TransactionReceipt> receipts = blockchain.getTxReceipts(header.getHash()).get(); final Block block = new Block(header, body); shortChain.appendBlock(block, receipts); nextBlock++; } return shortChain; }
private MutableBlockchain createShortChain( final Blockchain blockchain, final long truncateAtBlockNumber) { final BlockHeader genesisHeader = blockchain.getBlockHeader(BlockHeader.GENESIS_BLOCK_NUMBER).get(); final BlockBody genesisBody = blockchain.getBlockBody(genesisHeader.getHash()).get(); final Block genesisBlock = new Block(genesisHeader, genesisBody); final MutableBlockchain shortChain = createInMemoryBlockchain(genesisBlock); long nextBlock = genesisHeader.getNumber() + 1; while (nextBlock <= truncateAtBlockNumber) { final BlockHeader header = blockchain.getBlockHeader(nextBlock).get(); final BlockBody body = blockchain.getBlockBody(header.getHash()).get(); final List<TransactionReceipt> receipts = blockchain.getTxReceipts(header.getHash()).get(); final Block block = new Block(header, body); shortChain.appendBlock(block, receipts); nextBlock++; } return shortChain; } }
@Test public void networkWithOneValidatorIsAllowedToCreateConsecutiveBlocks() { final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); genesisBlock = createEmptyBlock(proposerKeyPair); blockChain = createInMemoryBlockchain(genesisBlock); final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); when(voteTallyCache.getVoteTallyAfterBlock(any())).thenReturn(new VoteTally(validatorList)); final VoteProposer voteProposer = new VoteProposer(); final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null); cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); headerBuilder.number(1).parentHash(genesisBlock.getHash()); final Block block_1 = createEmptyBlock(proposerKeyPair); blockChain.appendBlock(block_1, Lists.newArrayList()); assertThat( CliqueHelpers.addressIsAllowedToProduceNextBlock( localAddress, cliqueProtocolContext, block_1.getHeader())) .isTrue(); }
private MutableBlockchain createShortChain(final long truncateAtBlockNumber) { final BlockHeader genesisHeader = blockchain.getBlockHeader(BlockHeader.GENESIS_BLOCK_NUMBER).get(); final BlockBody genesisBody = blockchain.getBlockBody(genesisHeader.getHash()).get(); final Block genesisBlock = new Block(genesisHeader, genesisBody); final MutableBlockchain shortChain = createInMemoryBlockchain(genesisBlock); long nextBlock = genesisHeader.getNumber() + 1; while (nextBlock <= truncateAtBlockNumber) { final BlockHeader header = blockchain.getBlockHeader(nextBlock).get(); final BlockBody body = blockchain.getBlockBody(header.getHash()).get(); final List<TransactionReceipt> receipts = blockchain.getTxReceipts(header.getHash()).get(); final Block block = new Block(header, body); shortChain.appendBlock(block, receipts); nextBlock++; } return shortChain; } }
@Test public void signerIsValidIfInsufficientBlocksExistInHistory() { final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); validatorList.add(otherAddress); validatorList.add(AddressHelpers.ofValue(1)); validatorList.add(AddressHelpers.ofValue(2)); validatorList.add(AddressHelpers.ofValue(3)); // Should require 2 blocks between signings. genesisBlock = createEmptyBlock(proposerKeyPair); blockChain = createInMemoryBlockchain(genesisBlock); final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); when(voteTallyCache.getVoteTallyAfterBlock(any())).thenReturn(new VoteTally(validatorList)); final VoteProposer voteProposer = new VoteProposer(); final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null); cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); headerBuilder.parentHash(genesisBlock.getHash()).number(1); final Block block_1 = createEmptyBlock(otherNodeKeyPair); blockChain.appendBlock(block_1, Lists.newArrayList()); assertThat( CliqueHelpers.addressIsAllowedToProduceNextBlock( localAddress, cliqueProtocolContext, genesisBlock.getHeader())) .isTrue(); assertThat( CliqueHelpers.addressIsAllowedToProduceNextBlock( localAddress, cliqueProtocolContext, block_1.getHeader())) .isTrue(); }
@Test public void networkWithTwoValidatorsIsAllowedToProduceBlockIfNotPreviousBlockProposer() { final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); validatorList.add(otherAddress); genesisBlock = createEmptyBlock(otherNodeKeyPair); blockChain = createInMemoryBlockchain(genesisBlock); final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); when(voteTallyCache.getVoteTallyAfterBlock(any())).thenReturn(new VoteTally(validatorList)); final VoteProposer voteProposer = new VoteProposer(); final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null); cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); headerBuilder.number(1).parentHash(genesisBlock.getHash()); final Block block_1 = createEmptyBlock(proposerKeyPair); blockChain.appendBlock(block_1, Lists.newArrayList()); headerBuilder.number(2).parentHash(block_1.getHash()); final Block block_2 = createEmptyBlock(otherNodeKeyPair); blockChain.appendBlock(block_2, Lists.newArrayList()); assertThat( CliqueHelpers.addressIsAllowedToProduceNextBlock( localAddress, cliqueProtocolContext, block_1.getHeader())) .isFalse(); assertThat( CliqueHelpers.addressIsAllowedToProduceNextBlock( localAddress, cliqueProtocolContext, block_2.getHeader())) .isTrue(); }
@Test public void getHeadBlockNumber() { final BlockchainWithData data = setupBlockchain(3); final BlockchainQueries queries = data.blockchainQueries; long result = queries.headBlockNumber(); assertEquals(2L, result); // Increment and test final Block lastBlock = data.blockData.get(2).block; final Block nextBlock = gen.nextBlock(lastBlock); data.blockchain.appendBlock(nextBlock, gen.receipts(nextBlock)); // Check that number has incremented result = queries.headBlockNumber(); assertEquals(3L, result); }
@Test public void doesNotSyncToWorseChain() { localBlockchainSetup.importFirstBlocks(15); // Sanity check assertThat(localBlockchain.getChainHeadBlockNumber()) .isGreaterThan(BlockHeader.GENESIS_BLOCK_NUMBER); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain); final Responder responder = RespondingEthPeer.blockchainResponder(otherBlockchain); final FullSyncDownloader<?> downloader = downloader(); downloader.start(); peer.respond(responder); assertThat(syncState.syncTarget()).isNotPresent(); while (peer.hasOutstandingRequests()) { peer.respond(responder); } assertThat(syncState.syncTarget()).isNotPresent(); verify(localBlockchain, times(0)).appendBlock(any(), any()); }
@Test public void networkWithTwoValidatorsIsNotAllowedToProduceBlockIfIsPreviousBlockProposer() { final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); validatorList.add(otherAddress); genesisBlock = createEmptyBlock(proposerKeyPair); blockChain = createInMemoryBlockchain(genesisBlock); final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); when(voteTallyCache.getVoteTallyAfterBlock(any())).thenReturn(new VoteTally(validatorList)); final VoteProposer voteProposer = new VoteProposer(); final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null); cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); headerBuilder.parentHash(genesisBlock.getHash()).number(1); final Block block_1 = createEmptyBlock(proposerKeyPair); blockChain.appendBlock(block_1, Lists.newArrayList()); headerBuilder.parentHash(block_1.getHeader().getHash()).number(2); final BlockHeader block_2 = TestHelpers.createCliqueSignedBlockHeader(headerBuilder, proposerKeyPair, validatorList); final SignerRateLimitValidationRule validationRule = new SignerRateLimitValidationRule(); assertThat(validationRule.validate(block_2, block_1.getHeader(), cliqueProtocolContext)) .isFalse(); }
@BeforeClass public static void setupClass() { genesisBlock = blockDataGenerator.genesisBlock(); localBlockchain = createInMemoryBlockchain(genesisBlock); // Setup local chain for (int i = 1; i <= chainHeight; i++) { final BlockDataGenerator.BlockOptions options = new BlockDataGenerator.BlockOptions() .setBlockNumber(i) .setParentHash(localBlockchain.getBlockHashByNumber(i - 1).get()); final Block block = blockDataGenerator.block(options); final List<TransactionReceipt> receipts = blockDataGenerator.receipts(block); localBlockchain.appendBlock(block, receipts); } }
@BeforeClass public static void setupClass() { genesisBlock = blockDataGenerator.genesisBlock(); localBlockchain = createInMemoryBlockchain(genesisBlock); // Setup local chain. for (int i = 1; i <= chainHeight; i++) { final BlockDataGenerator.BlockOptions options = new BlockDataGenerator.BlockOptions() .setBlockNumber(i) .setParentHash(localBlockchain.getBlockHashByNumber(i - 1).get()); final Block block = blockDataGenerator.block(options); final List<TransactionReceipt> receipts = blockDataGenerator.receipts(block); localBlockchain.appendBlock(block, receipts); } }
@Before public void setup() { protocolSchedule = CliqueProtocolSchedule.create( GenesisConfigFile.DEFAULT.getConfigOptions(), proposerKeyPair); final Address otherAddress = Util.publicKeyToAddress(otherKeyPair.getPublicKey()); validatorList.add(otherAddress); final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); when(voteTallyCache.getVoteTallyAfterBlock(any())).thenReturn(new VoteTally(validatorList)); voteProposer = new VoteProposer(); final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null); final Block genesis = GenesisState.fromConfig(GenesisConfigFile.mainnet(), protocolSchedule).getBlock(); blockchain = createInMemoryBlockchain(genesis); protocolContext = new ProtocolContext<>(blockchain, stateArchive, cliqueContext); // Add a block above the genesis final BlockHeaderTestFixture headerTestFixture = new BlockHeaderTestFixture(); headerTestFixture.number(1).parentHash(genesis.getHeader().getHash()); final Block emptyBlock = new Block( TestHelpers.createCliqueSignedBlockHeader( headerTestFixture, otherKeyPair, validatorList), new BlockBody(Lists.newArrayList(), Lists.newArrayList())); blockchain.appendBlock(emptyBlock, Lists.newArrayList()); }
@Before public void constructThreeBlockChain() { for (int i = 0; i < 3; i++) { validators.add(AddressHelpers.ofValue(i)); } headerBuilder.extraData( new CliqueExtraData( BytesValue.wrap(new byte[32]), Signature.create(BigInteger.TEN, BigInteger.TEN, (byte) 1), validators) .encode()); genesisBlock = createEmptyBlock(0, Hash.ZERO); blockChain = createInMemoryBlockchain(genesisBlock); block_1 = createEmptyBlock(1, genesisBlock.getHeader().getHash()); block_2 = createEmptyBlock(1, block_1.getHeader().getHash()); blockChain.appendBlock(block_1, Lists.emptyList()); blockChain.appendBlock(block_2, Lists.emptyList()); }