mirror of https://github.com/hyperledger/besu
Clique Block Miner added (#11)
Responsible for mining a block if the local node address is a validator, and has not recently mined a block. This has necessitated a slight rework of helper functions, and shuffling of tests. Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
217750b5f0
commit
a754f57c01
@ -0,0 +1,42 @@ |
||||
package net.consensys.pantheon.consensus.clique.blockcreation; |
||||
|
||||
import net.consensys.pantheon.consensus.clique.CliqueContext; |
||||
import net.consensys.pantheon.consensus.clique.CliqueHelpers; |
||||
import net.consensys.pantheon.consensus.common.ValidatorProvider; |
||||
import net.consensys.pantheon.ethereum.ProtocolContext; |
||||
import net.consensys.pantheon.ethereum.blockcreation.AbstractBlockScheduler; |
||||
import net.consensys.pantheon.ethereum.blockcreation.BlockMiner; |
||||
import net.consensys.pantheon.ethereum.blockcreation.MiningCoordinator.MinedBlockObserver; |
||||
import net.consensys.pantheon.ethereum.core.Address; |
||||
import net.consensys.pantheon.ethereum.core.BlockHeader; |
||||
import net.consensys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||
import net.consensys.pantheon.util.Subscribers; |
||||
|
||||
public class CliqueBlockMiner extends BlockMiner<CliqueContext, CliqueBlockCreator> { |
||||
|
||||
private final Address localAddress; |
||||
|
||||
public CliqueBlockMiner( |
||||
final CliqueBlockCreator blockCreator, |
||||
final ProtocolSchedule<CliqueContext> protocolSchedule, |
||||
final ProtocolContext<CliqueContext> protocolContext, |
||||
final Subscribers<MinedBlockObserver> observers, |
||||
final AbstractBlockScheduler scheduler, |
||||
final BlockHeader parentHeader, |
||||
final Address localAddress) { |
||||
super(blockCreator, protocolSchedule, protocolContext, observers, scheduler, parentHeader); |
||||
this.localAddress = localAddress; |
||||
} |
||||
|
||||
@Override |
||||
protected boolean mineBlock() throws InterruptedException { |
||||
ValidatorProvider validators = |
||||
protocolContext.getConsensusState().getVoteTallyCache().getVoteTallyAtBlock(parentHeader); |
||||
if (CliqueHelpers.addressIsAllowedToProduceNextBlock( |
||||
localAddress, protocolContext, parentHeader)) { |
||||
return super.mineBlock(); |
||||
} |
||||
|
||||
return true; // terminate mining.
|
||||
} |
||||
} |
@ -0,0 +1,286 @@ |
||||
package net.consensys.pantheon.consensus.clique; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import net.consensys.pantheon.consensus.clique.headervalidationrules.SignerRateLimitValidationRule; |
||||
import net.consensys.pantheon.consensus.common.VoteProposer; |
||||
import net.consensys.pantheon.consensus.common.VoteTally; |
||||
import net.consensys.pantheon.crypto.SECP256K1.KeyPair; |
||||
import net.consensys.pantheon.ethereum.ProtocolContext; |
||||
import net.consensys.pantheon.ethereum.core.Address; |
||||
import net.consensys.pantheon.ethereum.core.AddressHelpers; |
||||
import net.consensys.pantheon.ethereum.core.Block; |
||||
import net.consensys.pantheon.ethereum.core.BlockBody; |
||||
import net.consensys.pantheon.ethereum.core.BlockHeader; |
||||
import net.consensys.pantheon.ethereum.core.BlockHeaderTestFixture; |
||||
import net.consensys.pantheon.ethereum.core.Hash; |
||||
import net.consensys.pantheon.ethereum.core.Util; |
||||
import net.consensys.pantheon.ethereum.db.DefaultMutableBlockchain; |
||||
import net.consensys.pantheon.ethereum.mainnet.MainnetBlockHashFunction; |
||||
import net.consensys.pantheon.services.kvstore.InMemoryKeyValueStorage; |
||||
import net.consensys.pantheon.services.kvstore.KeyValueStorage; |
||||
|
||||
import java.util.List; |
||||
|
||||
import com.google.common.collect.Lists; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
public class NodeCanProduceNextBlockTest { |
||||
|
||||
private final KeyPair proposerKeyPair = KeyPair.generate(); |
||||
private Address localAddress; |
||||
private final KeyPair otherNodeKeyPair = KeyPair.generate(); |
||||
private final List<Address> validatorList = Lists.newArrayList(); |
||||
private final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); |
||||
private ProtocolContext<CliqueContext> cliqueProtocolContext; |
||||
|
||||
DefaultMutableBlockchain blockChain; |
||||
private Block genesisBlock; |
||||
|
||||
private Block createEmptyBlock(final KeyPair blockSigner) { |
||||
final BlockHeader header = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, blockSigner, validatorList); |
||||
return new Block(header, new BlockBody(Lists.newArrayList(), Lists.newArrayList())); |
||||
} |
||||
|
||||
@Before |
||||
public void setup() { |
||||
localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); |
||||
validatorList.add(localAddress); |
||||
} |
||||
|
||||
@Test |
||||
public void networkWithOneValidatorIsAllowedToCreateConsecutiveBlocks() { |
||||
final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); |
||||
|
||||
genesisBlock = createEmptyBlock(proposerKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
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(); |
||||
} |
||||
|
||||
@Test |
||||
public void networkWithTwoValidatorsIsAllowedToProduceBlockIfNotPreviousBlockProposer() { |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(otherAddress); |
||||
|
||||
genesisBlock = createEmptyBlock(otherNodeKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
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 networkWithTwoValidatorsIsNotAllowedToProduceBlockIfIsPreviousBlockProposer() { |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(otherAddress); |
||||
|
||||
genesisBlock = createEmptyBlock(proposerKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
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(); |
||||
} |
||||
|
||||
@Test |
||||
public void withThreeValidatorsMustHaveOneBlockBetweenSignings() { |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(otherAddress); |
||||
validatorList.add(AddressHelpers.ofValue(1)); |
||||
|
||||
genesisBlock = createEmptyBlock(proposerKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
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.getHash()).number(2); |
||||
final Block block_2 = createEmptyBlock(otherNodeKeyPair); |
||||
blockChain.appendBlock(block_2, Lists.newArrayList()); |
||||
|
||||
headerBuilder.parentHash(block_2.getHash()).number(3); |
||||
final Block block_3 = createEmptyBlock(otherNodeKeyPair); |
||||
blockChain.appendBlock(block_3, Lists.newArrayList()); |
||||
|
||||
assertThat( |
||||
CliqueHelpers.addressIsAllowedToProduceNextBlock( |
||||
localAddress, cliqueProtocolContext, block_1.getHeader())) |
||||
.isFalse(); |
||||
assertThat( |
||||
CliqueHelpers.addressIsAllowedToProduceNextBlock( |
||||
localAddress, cliqueProtocolContext, block_2.getHeader())) |
||||
.isTrue(); |
||||
assertThat( |
||||
CliqueHelpers.addressIsAllowedToProduceNextBlock( |
||||
localAddress, cliqueProtocolContext, block_3.getHeader())) |
||||
.isTrue(); |
||||
} |
||||
|
||||
@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); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
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 exceptionIsThrownIfOnAnOrphanedChain() { |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(otherAddress); |
||||
|
||||
genesisBlock = createEmptyBlock(proposerKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); |
||||
|
||||
headerBuilder.parentHash(Hash.ZERO).number(3); |
||||
final BlockHeader parentHeader = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, otherNodeKeyPair, validatorList); |
||||
|
||||
assertThatThrownBy( |
||||
() -> |
||||
CliqueHelpers.addressIsAllowedToProduceNextBlock( |
||||
localAddress, cliqueProtocolContext, parentHeader)) |
||||
.isInstanceOf(RuntimeException.class) |
||||
.hasMessage("The block was on a orphaned chain."); |
||||
} |
||||
|
||||
@Test |
||||
public void nonValidatorIsNotAllowedToCreateABlock() { |
||||
genesisBlock = createEmptyBlock(otherNodeKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); |
||||
|
||||
headerBuilder.parentHash(Hash.ZERO).number(3); |
||||
final BlockHeader parentHeader = headerBuilder.buildHeader(); |
||||
assertThat( |
||||
CliqueHelpers.addressIsAllowedToProduceNextBlock( |
||||
AddressHelpers.ofValue(1), cliqueProtocolContext, parentHeader)) |
||||
.isFalse(); |
||||
} |
||||
} |
@ -1,258 +1,6 @@ |
||||
package net.consensys.pantheon.consensus.clique.headervalidationrules; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import net.consensys.pantheon.consensus.clique.CliqueContext; |
||||
import net.consensys.pantheon.consensus.clique.TestHelpers; |
||||
import net.consensys.pantheon.consensus.clique.VoteTallyCache; |
||||
import net.consensys.pantheon.consensus.common.VoteProposer; |
||||
import net.consensys.pantheon.consensus.common.VoteTally; |
||||
import net.consensys.pantheon.crypto.SECP256K1.KeyPair; |
||||
import net.consensys.pantheon.ethereum.ProtocolContext; |
||||
import net.consensys.pantheon.ethereum.core.Address; |
||||
import net.consensys.pantheon.ethereum.core.AddressHelpers; |
||||
import net.consensys.pantheon.ethereum.core.Block; |
||||
import net.consensys.pantheon.ethereum.core.BlockBody; |
||||
import net.consensys.pantheon.ethereum.core.BlockHeader; |
||||
import net.consensys.pantheon.ethereum.core.BlockHeaderTestFixture; |
||||
import net.consensys.pantheon.ethereum.core.Hash; |
||||
import net.consensys.pantheon.ethereum.core.Util; |
||||
import net.consensys.pantheon.ethereum.db.DefaultMutableBlockchain; |
||||
import net.consensys.pantheon.ethereum.mainnet.MainnetBlockHashFunction; |
||||
import net.consensys.pantheon.services.kvstore.InMemoryKeyValueStorage; |
||||
import net.consensys.pantheon.services.kvstore.KeyValueStorage; |
||||
|
||||
import java.util.List; |
||||
|
||||
import com.google.common.collect.Lists; |
||||
import org.junit.Test; |
||||
|
||||
public class SignerRateLimitValidationRuleTest { |
||||
|
||||
private final KeyPair proposerKeyPair = KeyPair.generate(); |
||||
private final KeyPair otherNodeKeyPair = KeyPair.generate(); |
||||
private final List<Address> validatorList = Lists.newArrayList(); |
||||
private final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); |
||||
private ProtocolContext<CliqueContext> cliqueProtocolContext; |
||||
|
||||
DefaultMutableBlockchain blockChain; |
||||
private Block genesisBlock; |
||||
|
||||
private Block createEmptyBlock(final KeyPair blockSigner) { |
||||
final BlockHeader header = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, blockSigner, validatorList); |
||||
return new Block(header, new BlockBody(Lists.newArrayList(), Lists.newArrayList())); |
||||
} |
||||
|
||||
@Test |
||||
public void networkWithOneValidatorIsAllowedToCreateConsecutiveBlocks() { |
||||
final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); |
||||
validatorList.add(localAddress); |
||||
|
||||
genesisBlock = createEmptyBlock(proposerKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); |
||||
|
||||
final BlockHeader nextBlockHeader = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, proposerKeyPair, validatorList); |
||||
|
||||
final SignerRateLimitValidationRule validationRule = new SignerRateLimitValidationRule(); |
||||
|
||||
assertThat( |
||||
validationRule.validate( |
||||
nextBlockHeader, genesisBlock.getHeader(), cliqueProtocolContext)) |
||||
.isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void networkWithTwoValidatorsIsAllowedToProduceBlockIfNotPreviousBlockProposer() { |
||||
final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(localAddress); |
||||
validatorList.add(otherAddress); |
||||
|
||||
genesisBlock = createEmptyBlock(otherNodeKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); |
||||
|
||||
final BlockHeader nextBlockHeader = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, proposerKeyPair, validatorList); |
||||
|
||||
final SignerRateLimitValidationRule validationRule = new SignerRateLimitValidationRule(); |
||||
|
||||
assertThat( |
||||
validationRule.validate( |
||||
nextBlockHeader, genesisBlock.getHeader(), cliqueProtocolContext)) |
||||
.isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void networkWithTwoValidatorsIsNotAllowedToProduceBlockIfIsPreviousBlockProposer() { |
||||
final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(localAddress); |
||||
validatorList.add(otherAddress); |
||||
|
||||
genesisBlock = createEmptyBlock(proposerKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
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(); |
||||
} |
||||
|
||||
@Test |
||||
public void withThreeValidatorsMustHaveOneBlockBetweenSignings() { |
||||
final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(localAddress); |
||||
validatorList.add(otherAddress); |
||||
validatorList.add(AddressHelpers.ofValue(1)); |
||||
|
||||
genesisBlock = createEmptyBlock(proposerKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
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.getHash()).number(2); |
||||
final Block block_2 = createEmptyBlock(otherNodeKeyPair); |
||||
blockChain.appendBlock(block_1, Lists.newArrayList()); |
||||
|
||||
final SignerRateLimitValidationRule validationRule = new SignerRateLimitValidationRule(); |
||||
BlockHeader nextBlockHeader; |
||||
|
||||
// Should not be able to proposer ontop of Block_1 (which has the same sealer)
|
||||
headerBuilder.parentHash(block_1.getHash()).number(2); |
||||
nextBlockHeader = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, proposerKeyPair, validatorList); |
||||
assertThat(validationRule.validate(nextBlockHeader, block_1.getHeader(), cliqueProtocolContext)) |
||||
.isFalse(); |
||||
|
||||
headerBuilder.parentHash(block_1.getHash()).number(3); |
||||
nextBlockHeader = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, proposerKeyPair, validatorList); |
||||
assertThat(validationRule.validate(nextBlockHeader, block_2.getHeader(), cliqueProtocolContext)) |
||||
.isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void signerIsValidIfInsufficientBlocksExistInHistory() { |
||||
final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(localAddress); |
||||
validatorList.add(otherAddress); |
||||
validatorList.add(AddressHelpers.ofValue(1)); |
||||
|
||||
genesisBlock = createEmptyBlock(otherNodeKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); |
||||
|
||||
final BlockHeader nextBlockHeader = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, proposerKeyPair, validatorList); |
||||
|
||||
final SignerRateLimitValidationRule validationRule = new SignerRateLimitValidationRule(); |
||||
|
||||
assertThat( |
||||
validationRule.validate( |
||||
nextBlockHeader, genesisBlock.getHeader(), cliqueProtocolContext)) |
||||
.isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void exceptionIsThrownIfOnAnOrphanedChain() { |
||||
final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); |
||||
final Address otherAddress = Util.publicKeyToAddress(otherNodeKeyPair.getPublicKey()); |
||||
validatorList.add(localAddress); |
||||
validatorList.add(otherAddress); |
||||
|
||||
genesisBlock = createEmptyBlock(otherNodeKeyPair); |
||||
|
||||
final KeyValueStorage keyValueStorage = new InMemoryKeyValueStorage(); |
||||
blockChain = |
||||
new DefaultMutableBlockchain( |
||||
genesisBlock, keyValueStorage, MainnetBlockHashFunction::createHash); |
||||
|
||||
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); |
||||
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); |
||||
final VoteProposer voteProposer = new VoteProposer(); |
||||
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer); |
||||
cliqueProtocolContext = new ProtocolContext<>(blockChain, null, cliqueContext); |
||||
|
||||
headerBuilder.parentHash(Hash.ZERO).number(4); |
||||
final BlockHeader nextBlock = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, proposerKeyPair, validatorList); |
||||
|
||||
headerBuilder.parentHash(Hash.ZERO).number(3); |
||||
final BlockHeader parentHeader = |
||||
TestHelpers.createCliqueSignedBlockHeader(headerBuilder, otherNodeKeyPair, validatorList); |
||||
|
||||
final SignerRateLimitValidationRule validationRule = new SignerRateLimitValidationRule(); |
||||
|
||||
assertThatThrownBy( |
||||
() -> validationRule.validate(nextBlock, parentHeader, cliqueProtocolContext)) |
||||
.isInstanceOf(RuntimeException.class) |
||||
.hasMessage("The block was on a orphaned chain."); |
||||
} |
||||
// Implicitly conducted by NodeCanProduceNextBlockTest.
|
||||
} |
||||
|
Loading…
Reference in new issue