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; |
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 { |
public class SignerRateLimitValidationRuleTest { |
||||||
|
|
||||||
private final KeyPair proposerKeyPair = KeyPair.generate(); |
// Implicitly conducted by NodeCanProduceNextBlockTest.
|
||||||
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."); |
|
||||||
} |
|
||||||
} |
} |
||||||
|
Loading…
Reference in new issue