Repair Clique Proposer Selection (#339)

Clique Proposer Selection would choose an incorrect peer
when a signer was removed from the pool, as the algorithm worked
purely on the block count.

The algorithm has now been updated to ensure the next proposer is
incrementally the next signer in the ordered list, based on the
parent's proposer
tmohay 6 years ago committed by GitHub
parent 566a4bf76e
commit b8dbfd50fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockHashing.java
  2. 29
      consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueProposerSelector.java
  3. 20
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueDifficultyCalculatorTest.java
  4. 28
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockSchedulerTest.java
  5. 71
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueProposerSelectorTest.java
  6. 78
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/headervalidationrules/CliqueDifficultyValidationRuleTest.java
  7. 13
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/jsonrpc/methods/CliqueGetSignersAtHashTest.java
  8. 13
      consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/jsonrpc/methods/CliqueGetSignersTest.java
  9. 4
      consensus/common/src/main/java/tech/pegasys/pantheon/consensus/common/ValidatorProvider.java
  10. 6
      consensus/common/src/main/java/tech/pegasys/pantheon/consensus/common/VoteTally.java
  11. 4
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/network/IbftNetworkPeersTest.java

@ -50,7 +50,7 @@ public class CliqueBlockHashing {
final BlockHeader header, final CliqueExtraData cliqueExtraData) { final BlockHeader header, final CliqueExtraData cliqueExtraData) {
if (!cliqueExtraData.getProposerSeal().isPresent()) { if (!cliqueExtraData.getProposerSeal().isPresent()) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Supplied cliqueExtraData does not include a proposer " + "seal"); "Supplied cliqueExtraData does not include a proposer seal.");
} }
final Hash proposerHash = calculateDataHashForProposerSeal(header, cliqueExtraData); final Hash proposerHash = calculateDataHashForProposerSeal(header, cliqueExtraData);
return Util.signatureToAddress(cliqueExtraData.getProposerSeal().get(), proposerHash); return Util.signatureToAddress(cliqueExtraData.getProposerSeal().get(), proposerHash);

@ -14,13 +14,14 @@ package tech.pegasys.pantheon.consensus.clique.blockcreation;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import tech.pegasys.pantheon.consensus.clique.CliqueBlockInterface;
import tech.pegasys.pantheon.consensus.clique.VoteTallyCache; import tech.pegasys.pantheon.consensus.clique.VoteTallyCache;
import tech.pegasys.pantheon.consensus.common.VoteTally; import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import java.util.ArrayList; import java.util.NavigableSet;
import java.util.List; import java.util.SortedSet;
/** /**
* Responsible for determining which member of the validator pool should create the next block. * Responsible for determining which member of the validator pool should create the next block.
@ -31,6 +32,7 @@ import java.util.List;
public class CliqueProposerSelector { public class CliqueProposerSelector {
private final VoteTallyCache voteTallyCache; private final VoteTallyCache voteTallyCache;
private final CliqueBlockInterface blockInterface = new CliqueBlockInterface();
public CliqueProposerSelector(final VoteTallyCache voteTallyCache) { public CliqueProposerSelector(final VoteTallyCache voteTallyCache) {
checkNotNull(voteTallyCache); checkNotNull(voteTallyCache);
@ -46,11 +48,26 @@ public class CliqueProposerSelector {
public Address selectProposerForNextBlock(final BlockHeader parentHeader) { public Address selectProposerForNextBlock(final BlockHeader parentHeader) {
final VoteTally parentVoteTally = voteTallyCache.getVoteTallyAtBlock(parentHeader); final VoteTally parentVoteTally = voteTallyCache.getVoteTallyAtBlock(parentHeader);
final List<Address> validatorSet = new ArrayList<>(parentVoteTally.getCurrentValidators()); final NavigableSet<Address> validatorSet = parentVoteTally.getCurrentValidators();
final long nextBlockNumber = parentHeader.getNumber() + 1L; Address prevBlockProposer = validatorSet.first();
final int indexIntoValidators = (int) (nextBlockNumber % validatorSet.size()); if (parentHeader.getNumber() != BlockHeader.GENESIS_BLOCK_NUMBER) {
prevBlockProposer = blockInterface.getProposerOfBlock(parentHeader);
}
return validatorSet.get(indexIntoValidators); return selectNextProposer(prevBlockProposer, validatorSet);
}
private Address selectNextProposer(
final Address prevBlockProposer, final NavigableSet<Address> validatorSet) {
final SortedSet<Address> latterValidators = validatorSet.tailSet(prevBlockProposer, false);
if (latterValidators.isEmpty()) {
// i.e. prevBlockProposer was at the end of the validator list, so the right validator for
// the start of this round is the first.
return validatorSet.first();
} else {
// Else, use the first validator after the dropped entry.
return latterValidators.first();
}
} }
} }

@ -16,6 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static tech.pegasys.pantheon.consensus.clique.TestHelpers.createCliqueSignedBlockHeader;
import tech.pegasys.pantheon.consensus.common.VoteProposer; import tech.pegasys.pantheon.consensus.common.VoteProposer;
import tech.pegasys.pantheon.consensus.common.VoteTally; import tech.pegasys.pantheon.consensus.common.VoteTally;
@ -57,25 +58,30 @@ public class CliqueDifficultyCalculatorTest {
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null); final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null);
cliqueProtocolContext = new ProtocolContext<>(null, null, cliqueContext); cliqueProtocolContext = new ProtocolContext<>(null, null, cliqueContext);
blockHeaderBuilder = new BlockHeaderTestFixture(); blockHeaderBuilder = new BlockHeaderTestFixture();
blockHeaderBuilder.number(1);
} }
@Test @Test
public void inTurnValidatorProducesDifficultyOfTwo() { public void outTurnValidatorProducesDifficultyOfOne() {
// The proposer created the last block, so the next block must be a difficulty of 1.
final CliqueDifficultyCalculator calculator = new CliqueDifficultyCalculator(localAddr); final CliqueDifficultyCalculator calculator = new CliqueDifficultyCalculator(localAddr);
final BlockHeader parentHeader = blockHeaderBuilder.number(1).buildHeader(); BlockHeader parentHeader =
createCliqueSignedBlockHeader(blockHeaderBuilder, proposerKeyPair, validatorList);
assertThat(calculator.nextDifficulty(0, parentHeader, cliqueProtocolContext)) assertThat(calculator.nextDifficulty(0, parentHeader, cliqueProtocolContext))
.isEqualTo(BigInteger.valueOf(2)); .isEqualTo(BigInteger.valueOf(1));
} }
@Test @Test
public void outTurnValidatorProducesDifficultyOfOne() { public void inTurnValidatorCreatesDifficultyOfTwo() {
final CliqueDifficultyCalculator calculator = new CliqueDifficultyCalculator(localAddr); final CliqueDifficultyCalculator calculator =
new CliqueDifficultyCalculator(validatorList.get(1)); // i.e. not the proposer.
final BlockHeader parentHeader = blockHeaderBuilder.number(2).buildHeader(); BlockHeader parentHeader =
createCliqueSignedBlockHeader(blockHeaderBuilder, proposerKeyPair, validatorList);
assertThat(calculator.nextDifficulty(0, parentHeader, cliqueProtocolContext)) assertThat(calculator.nextDifficulty(0, parentHeader, cliqueProtocolContext))
.isEqualTo(BigInteger.valueOf(1)); .isEqualTo(BigInteger.valueOf(2));
} }
} }

@ -16,6 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static tech.pegasys.pantheon.consensus.clique.TestHelpers.createCliqueSignedBlockHeader;
import tech.pegasys.pantheon.consensus.clique.VoteTallyCache; import tech.pegasys.pantheon.consensus.clique.VoteTallyCache;
import tech.pegasys.pantheon.consensus.common.VoteTally; import tech.pegasys.pantheon.consensus.common.VoteTally;
@ -37,7 +38,8 @@ import org.junit.Test;
public class CliqueBlockSchedulerTest { public class CliqueBlockSchedulerTest {
private final KeyPair proposerKeyPair = KeyPair.generate(); private final KeyPair proposerKeyPair = KeyPair.generate();
private Address localAddr; private Address localAddr = Util.publicKeyToAddress(proposerKeyPair.getPublicKey());
private Address otherAddr = AddressHelpers.calculateAddressWithRespectTo(localAddr, 1);
private final List<Address> validatorList = Lists.newArrayList(); private final List<Address> validatorList = Lists.newArrayList();
private VoteTallyCache voteTallyCache; private VoteTallyCache voteTallyCache;
@ -45,10 +47,9 @@ public class CliqueBlockSchedulerTest {
@Before @Before
public void setup() { public void setup() {
localAddr = Util.publicKeyToAddress(proposerKeyPair.getPublicKey());
validatorList.add(localAddr); validatorList.add(localAddr);
validatorList.add(AddressHelpers.calculateAddressWithRespectTo(localAddr, 1)); validatorList.add(otherAddr);
voteTallyCache = mock(VoteTallyCache.class); voteTallyCache = mock(VoteTallyCache.class);
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList));
@ -63,12 +64,11 @@ public class CliqueBlockSchedulerTest {
final long secondsBetweenBlocks = 5L; final long secondsBetweenBlocks = 5L;
when(clock.millis()).thenReturn(currentSecondsSinceEpoch * 1000); when(clock.millis()).thenReturn(currentSecondsSinceEpoch * 1000);
final CliqueBlockScheduler scheduler = final CliqueBlockScheduler scheduler =
new CliqueBlockScheduler(clock, voteTallyCache, localAddr, secondsBetweenBlocks); new CliqueBlockScheduler(clock, voteTallyCache, otherAddr, secondsBetweenBlocks);
// There are 2 validators, therefore block 2 will put localAddr as the in-turn voter, therefore blockHeaderBuilder.number(1).timestamp(currentSecondsSinceEpoch);
// parent block should be number 1.
final BlockHeader parentHeader = final BlockHeader parentHeader =
blockHeaderBuilder.number(1).timestamp(currentSecondsSinceEpoch).buildHeader(); createCliqueSignedBlockHeader(blockHeaderBuilder, proposerKeyPair, validatorList);
final BlockCreationTimeResult result = scheduler.getNextTimestamp(parentHeader); final BlockCreationTimeResult result = scheduler.getNextTimestamp(parentHeader);
@ -86,10 +86,9 @@ public class CliqueBlockSchedulerTest {
final CliqueBlockScheduler scheduler = final CliqueBlockScheduler scheduler =
new CliqueBlockScheduler(clock, voteTallyCache, localAddr, secondsBetweenBlocks); new CliqueBlockScheduler(clock, voteTallyCache, localAddr, secondsBetweenBlocks);
// There are 2 validators, therefore block 3 will put localAddr as the out-turn voter, therefore blockHeaderBuilder.number(2).timestamp(currentSecondsSinceEpoch);
// parent block should be number 2.
final BlockHeader parentHeader = final BlockHeader parentHeader =
blockHeaderBuilder.number(2).timestamp(currentSecondsSinceEpoch).buildHeader(); createCliqueSignedBlockHeader(blockHeaderBuilder, proposerKeyPair, validatorList);
final BlockCreationTimeResult result = scheduler.getNextTimestamp(parentHeader); final BlockCreationTimeResult result = scheduler.getNextTimestamp(parentHeader);
@ -105,16 +104,13 @@ public class CliqueBlockSchedulerTest {
final long secondsBetweenBlocks = 5L; final long secondsBetweenBlocks = 5L;
when(clock.millis()).thenReturn(currentSecondsSinceEpoch * 1000); when(clock.millis()).thenReturn(currentSecondsSinceEpoch * 1000);
final CliqueBlockScheduler scheduler = final CliqueBlockScheduler scheduler =
new CliqueBlockScheduler(clock, voteTallyCache, localAddr, secondsBetweenBlocks); new CliqueBlockScheduler(clock, voteTallyCache, otherAddr, secondsBetweenBlocks);
// There are 2 validators, therefore block 2 will put localAddr as the in-turn voter, therefore // There are 2 validators, therefore block 2 will put localAddr as the in-turn voter, therefore
// parent block should be number 1. // parent block should be number 1.
blockHeaderBuilder.number(1).timestamp(currentSecondsSinceEpoch - secondsBetweenBlocks);
final BlockHeader parentHeader = final BlockHeader parentHeader =
blockHeaderBuilder createCliqueSignedBlockHeader(blockHeaderBuilder, proposerKeyPair, validatorList);
.number(1)
.timestamp(currentSecondsSinceEpoch - secondsBetweenBlocks)
.buildHeader();
final BlockCreationTimeResult result = scheduler.getNextTimestamp(parentHeader); final BlockCreationTimeResult result = scheduler.getNextTimestamp(parentHeader);
assertThat(result.getTimestampForHeader()).isEqualTo(currentSecondsSinceEpoch); assertThat(result.getTimestampForHeader()).isEqualTo(currentSecondsSinceEpoch);

@ -17,46 +17,87 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.consensus.clique.TestHelpers;
import tech.pegasys.pantheon.consensus.clique.VoteTallyCache; import tech.pegasys.pantheon.consensus.clique.VoteTallyCache;
import tech.pegasys.pantheon.consensus.common.VoteTally; import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.AddressHelpers; import tech.pegasys.pantheon.ethereum.core.AddressHelpers;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture; import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture;
import tech.pegasys.pantheon.ethereum.core.Util;
import java.util.Arrays;
import java.util.List; import java.util.List;
import com.google.common.collect.Lists;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
public class CliqueProposerSelectorTest { public class CliqueProposerSelectorTest {
private final KeyPair proposerKey = KeyPair.generate();
private final Address proposerAddress = Util.publicKeyToAddress(proposerKey.getPublicKey());
private final List<Address> validatorList = private final List<Address> validatorList =
Arrays.asList( Lists.newArrayList(
AddressHelpers.ofValue(1), AddressHelpers.calculateAddressWithRespectTo(proposerAddress, -1),
AddressHelpers.ofValue(2), proposerAddress,
AddressHelpers.ofValue(3), AddressHelpers.calculateAddressWithRespectTo(proposerAddress, 1),
AddressHelpers.ofValue(4)); AddressHelpers.calculateAddressWithRespectTo(proposerAddress, 2));
private final VoteTally voteTally = new VoteTally(validatorList); private final VoteTally voteTally = new VoteTally(validatorList);
private VoteTallyCache voteTallyCache; private VoteTallyCache voteTallyCache;
private final BlockHeaderTestFixture headerBuilderFixture = new BlockHeaderTestFixture();
@Before @Before
public void setup() { public void setup() {
voteTallyCache = mock(VoteTallyCache.class); voteTallyCache = mock(VoteTallyCache.class);
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(voteTally); when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(voteTally);
headerBuilderFixture.number(2);
} }
@Test @Test
public void proposerForABlockIsBasedOnModBlockNumber() { public void firstBlockAfterGenesisIsTheSecondValidator() {
final BlockHeaderTestFixture headerBuilderFixture = new BlockHeaderTestFixture(); final BlockHeaderTestFixture headerBuilderFixture = new BlockHeaderTestFixture();
final CliqueProposerSelector selector = new CliqueProposerSelector(voteTallyCache);
headerBuilderFixture.number(0);
assertThat(selector.selectProposerForNextBlock(headerBuilderFixture.buildHeader()))
.isEqualTo(validatorList.get(1));
}
@Test
public void selectsNextProposerInValidatorSet() {
final BlockHeader parentHeader =
TestHelpers.createCliqueSignedBlockHeader(headerBuilderFixture, proposerKey, validatorList);
final CliqueProposerSelector selector = new CliqueProposerSelector(voteTallyCache);
// Proposer is at index 1, so the next proposer is at index 2
assertThat(selector.selectProposerForNextBlock(parentHeader)).isEqualTo(validatorList.get(2));
}
@Test
public void selectsNextIndexWhenProposerIsNotInValidatorsForBlock() {
final BlockHeader parentHeader =
TestHelpers.createCliqueSignedBlockHeader(headerBuilderFixture, proposerKey, validatorList);
final CliqueProposerSelector selector = new CliqueProposerSelector(voteTallyCache);
validatorList.remove(proposerAddress);
// As the proposer was removed (index 1), the next proposer should also be index 1
assertThat(selector.selectProposerForNextBlock(parentHeader)).isEqualTo(validatorList.get(1));
}
@Test
public void singleValidatorFindsItselfAsNextProposer() {
final List<Address> localValidators = Lists.newArrayList(proposerAddress);
final VoteTally localVoteTally = new VoteTally(localValidators);
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(localVoteTally);
final BlockHeader parentHeader =
TestHelpers.createCliqueSignedBlockHeader(headerBuilderFixture, proposerKey, validatorList);
final CliqueProposerSelector selector = new CliqueProposerSelector(voteTallyCache);
for (int prevBlockNumber = 0; prevBlockNumber < 10; prevBlockNumber++) { assertThat(selector.selectProposerForNextBlock(parentHeader)).isEqualTo(proposerAddress);
headerBuilderFixture.number(prevBlockNumber);
final CliqueProposerSelector selector = new CliqueProposerSelector(voteTallyCache);
final Address nextProposer =
selector.selectProposerForNextBlock(headerBuilderFixture.buildHeader());
assertThat(nextProposer)
.isEqualTo(validatorList.get((prevBlockNumber + 1) % validatorList.size()));
}
} }
} }

@ -17,23 +17,17 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.consensus.clique.CliqueBlockHashing;
import tech.pegasys.pantheon.consensus.clique.CliqueContext; import tech.pegasys.pantheon.consensus.clique.CliqueContext;
import tech.pegasys.pantheon.consensus.clique.CliqueExtraData; import tech.pegasys.pantheon.consensus.clique.TestHelpers;
import tech.pegasys.pantheon.consensus.clique.VoteTallyCache; import tech.pegasys.pantheon.consensus.clique.VoteTallyCache;
import tech.pegasys.pantheon.consensus.common.VoteProposer; import tech.pegasys.pantheon.consensus.common.VoteProposer;
import tech.pegasys.pantheon.consensus.common.VoteTally; import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.crypto.SECP256K1;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.ProtocolContext; import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.AddressHelpers;
import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture; import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.Util; import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.uint.UInt256; import tech.pegasys.pantheon.util.uint.UInt256;
import java.util.List; import java.util.List;
@ -45,15 +39,15 @@ import org.junit.Test;
public class CliqueDifficultyValidationRuleTest { public class CliqueDifficultyValidationRuleTest {
private final KeyPair proposerKeyPair = KeyPair.generate(); private final KeyPair proposerKeyPair = KeyPair.generate();
private final KeyPair otherKeyPair = KeyPair.generate();
private final List<Address> validatorList = Lists.newArrayList(); private final List<Address> validatorList = Lists.newArrayList();
private ProtocolContext<CliqueContext> cliqueProtocolContext; private ProtocolContext<CliqueContext> cliqueProtocolContext;
private BlockHeaderTestFixture blockHeaderBuilder; private BlockHeaderTestFixture blockHeaderBuilder;
@Before @Before
public void setup() { public void setup() {
final Address localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey()); validatorList.add(Util.publicKeyToAddress(proposerKeyPair.getPublicKey()));
validatorList.add(localAddress); validatorList.add(Util.publicKeyToAddress(otherKeyPair.getPublicKey()));
validatorList.add(AddressHelpers.calculateAddressWithRespectTo(localAddress, 1));
final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class); final VoteTallyCache voteTallyCache = mock(VoteTallyCache.class);
when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList)); when(voteTallyCache.getVoteTallyAtBlock(any())).thenReturn(new VoteTally(validatorList));
@ -62,29 +56,7 @@ public class CliqueDifficultyValidationRuleTest {
final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null); final CliqueContext cliqueContext = new CliqueContext(voteTallyCache, voteProposer, null);
cliqueProtocolContext = new ProtocolContext<>(null, null, cliqueContext); cliqueProtocolContext = new ProtocolContext<>(null, null, cliqueContext);
blockHeaderBuilder = new BlockHeaderTestFixture(); blockHeaderBuilder = new BlockHeaderTestFixture();
} blockHeaderBuilder.number(2);
private BlockHeader createCliqueSignedBlock(final BlockHeaderTestFixture blockHeaderBuilder) {
final CliqueExtraData unsignedExtraData =
new CliqueExtraData(BytesValue.wrap(new byte[32]), null, validatorList);
blockHeaderBuilder.extraData(unsignedExtraData.encode());
final Hash signingHash =
CliqueBlockHashing.calculateDataHashForProposerSeal(
blockHeaderBuilder.buildHeader(), unsignedExtraData);
final Signature proposerSignature = SECP256K1.sign(signingHash, proposerKeyPair);
final CliqueExtraData signedExtraData =
new CliqueExtraData(
unsignedExtraData.getVanityData(),
proposerSignature,
unsignedExtraData.getValidators());
blockHeaderBuilder.extraData(signedExtraData.encode());
return blockHeaderBuilder.buildHeader();
} }
@Test @Test
@ -92,11 +64,13 @@ public class CliqueDifficultyValidationRuleTest {
final long IN_TURN_BLOCK_NUMBER = validatorList.size(); // i.e. proposer is 'in turn' final long IN_TURN_BLOCK_NUMBER = validatorList.size(); // i.e. proposer is 'in turn'
final UInt256 REPORTED_DIFFICULTY = UInt256.of(2); final UInt256 REPORTED_DIFFICULTY = UInt256.of(2);
blockHeaderBuilder.number(IN_TURN_BLOCK_NUMBER - 1L); final BlockHeader parentHeader =
final BlockHeader parentHeader = createCliqueSignedBlock(blockHeaderBuilder); TestHelpers.createCliqueSignedBlockHeader(blockHeaderBuilder, otherKeyPair, validatorList);
blockHeaderBuilder.number(IN_TURN_BLOCK_NUMBER).difficulty(REPORTED_DIFFICULTY); blockHeaderBuilder.number(IN_TURN_BLOCK_NUMBER).difficulty(REPORTED_DIFFICULTY);
final BlockHeader newBlock = createCliqueSignedBlock(blockHeaderBuilder); final BlockHeader newBlock =
TestHelpers.createCliqueSignedBlockHeader(
blockHeaderBuilder, proposerKeyPair, validatorList);
final CliqueDifficultyValidationRule diffValidationRule = new CliqueDifficultyValidationRule(); final CliqueDifficultyValidationRule diffValidationRule = new CliqueDifficultyValidationRule();
assertThat(diffValidationRule.validate(newBlock, parentHeader, cliqueProtocolContext)).isTrue(); assertThat(diffValidationRule.validate(newBlock, parentHeader, cliqueProtocolContext)).isTrue();
@ -107,11 +81,14 @@ public class CliqueDifficultyValidationRuleTest {
final long OUT_OF_TURN_BLOCK_NUMBER = validatorList.size() - 1L; final long OUT_OF_TURN_BLOCK_NUMBER = validatorList.size() - 1L;
final UInt256 REPORTED_DIFFICULTY = UInt256.of(1); final UInt256 REPORTED_DIFFICULTY = UInt256.of(1);
blockHeaderBuilder.number(OUT_OF_TURN_BLOCK_NUMBER - 1L); final BlockHeader parentHeader =
final BlockHeader parentHeader = createCliqueSignedBlock(blockHeaderBuilder); TestHelpers.createCliqueSignedBlockHeader(
blockHeaderBuilder, proposerKeyPair, validatorList);
blockHeaderBuilder.number(OUT_OF_TURN_BLOCK_NUMBER).difficulty(REPORTED_DIFFICULTY); blockHeaderBuilder.number(OUT_OF_TURN_BLOCK_NUMBER).difficulty(REPORTED_DIFFICULTY);
final BlockHeader newBlock = createCliqueSignedBlock(blockHeaderBuilder); final BlockHeader newBlock =
TestHelpers.createCliqueSignedBlockHeader(
blockHeaderBuilder, proposerKeyPair, validatorList);
final CliqueDifficultyValidationRule diffValidationRule = new CliqueDifficultyValidationRule(); final CliqueDifficultyValidationRule diffValidationRule = new CliqueDifficultyValidationRule();
assertThat(diffValidationRule.validate(newBlock, parentHeader, cliqueProtocolContext)).isTrue(); assertThat(diffValidationRule.validate(newBlock, parentHeader, cliqueProtocolContext)).isTrue();
@ -119,14 +96,16 @@ public class CliqueDifficultyValidationRuleTest {
@Test @Test
public void isFalseIfOutTurnValidatorSuppliesDifficultyOfTwo() { public void isFalseIfOutTurnValidatorSuppliesDifficultyOfTwo() {
final long OUT_OF_TURN_BLOCK_NUMBER = validatorList.size() - 1L;
final UInt256 REPORTED_DIFFICULTY = UInt256.of(2); final UInt256 REPORTED_DIFFICULTY = UInt256.of(2);
blockHeaderBuilder.number(OUT_OF_TURN_BLOCK_NUMBER - 1L); final BlockHeader parentHeader =
final BlockHeader parentHeader = createCliqueSignedBlock(blockHeaderBuilder); TestHelpers.createCliqueSignedBlockHeader(
blockHeaderBuilder, proposerKeyPair, validatorList);
blockHeaderBuilder.number(OUT_OF_TURN_BLOCK_NUMBER).difficulty(REPORTED_DIFFICULTY); blockHeaderBuilder.difficulty(REPORTED_DIFFICULTY);
final BlockHeader newBlock = createCliqueSignedBlock(blockHeaderBuilder); final BlockHeader newBlock =
TestHelpers.createCliqueSignedBlockHeader(
blockHeaderBuilder, proposerKeyPair, validatorList);
final CliqueDifficultyValidationRule diffValidationRule = new CliqueDifficultyValidationRule(); final CliqueDifficultyValidationRule diffValidationRule = new CliqueDifficultyValidationRule();
assertThat(diffValidationRule.validate(newBlock, parentHeader, cliqueProtocolContext)) assertThat(diffValidationRule.validate(newBlock, parentHeader, cliqueProtocolContext))
@ -135,14 +114,15 @@ public class CliqueDifficultyValidationRuleTest {
@Test @Test
public void isFalseIfInTurnValidatorSuppliesDifficultyOfOne() { public void isFalseIfInTurnValidatorSuppliesDifficultyOfOne() {
final long IN_TURN_BLOCK_NUMBER = validatorList.size();
final UInt256 REPORTED_DIFFICULTY = UInt256.of(1); final UInt256 REPORTED_DIFFICULTY = UInt256.of(1);
blockHeaderBuilder.number(IN_TURN_BLOCK_NUMBER - 1L); final BlockHeader parentHeader =
final BlockHeader parentHeader = createCliqueSignedBlock(blockHeaderBuilder); TestHelpers.createCliqueSignedBlockHeader(blockHeaderBuilder, otherKeyPair, validatorList);
blockHeaderBuilder.number(IN_TURN_BLOCK_NUMBER).difficulty(REPORTED_DIFFICULTY); blockHeaderBuilder.difficulty(REPORTED_DIFFICULTY);
final BlockHeader newBlock = createCliqueSignedBlock(blockHeaderBuilder); final BlockHeader newBlock =
TestHelpers.createCliqueSignedBlockHeader(
blockHeaderBuilder, proposerKeyPair, validatorList);
final CliqueDifficultyValidationRule diffValidationRule = new CliqueDifficultyValidationRule(); final CliqueDifficultyValidationRule diffValidationRule = new CliqueDifficultyValidationRule();
assertThat(diffValidationRule.validate(newBlock, parentHeader, cliqueProtocolContext)) assertThat(diffValidationRule.validate(newBlock, parentHeader, cliqueProtocolContext))

@ -37,7 +37,9 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessRe
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.List; import java.util.List;
import java.util.NavigableSet;
import java.util.Optional; import java.util.Optional;
import java.util.TreeSet;
import org.assertj.core.api.AssertionsForClassTypes; import org.assertj.core.api.AssertionsForClassTypes;
import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
@ -52,7 +54,7 @@ public class CliqueGetSignersAtHashTest {
private CliqueGetSignersAtHash method; private CliqueGetSignersAtHash method;
private BlockHeader blockHeader; private BlockHeader blockHeader;
private List<Address> validators; private NavigableSet<Address> validators;
private List<String> validatorsAsStrings; private List<String> validatorsAsStrings;
@Mock private BlockchainQueries blockchainQueries; @Mock private BlockchainQueries blockchainQueries;
@ -75,10 +77,11 @@ public class CliqueGetSignersAtHashTest {
blockHeader = blockHeaderTestFixture.extraData(bufferToInject).buildHeader(); blockHeader = blockHeaderTestFixture.extraData(bufferToInject).buildHeader();
validators = validators =
asList( new TreeSet<>(
fromHexString("0x42eb768f2244c8811c63729a21a3569731535f06"), asList(
fromHexString("0x7ffc57839b00206d1ad20c69a1981b489f772031"), fromHexString("0x42eb768f2244c8811c63729a21a3569731535f06"),
fromHexString("0xb279182d99e65703f0076e4812653aab85fca0f0")); fromHexString("0x7ffc57839b00206d1ad20c69a1981b489f772031"),
fromHexString("0xb279182d99e65703f0076e4812653aab85fca0f0")));
validatorsAsStrings = validators.stream().map(Object::toString).collect(toList()); validatorsAsStrings = validators.stream().map(Object::toString).collect(toList());
} }

@ -36,7 +36,9 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessRe
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.List; import java.util.List;
import java.util.NavigableSet;
import java.util.Optional; import java.util.Optional;
import java.util.TreeSet;
import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
import org.junit.Before; import org.junit.Before;
@ -49,7 +51,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class CliqueGetSignersTest { public class CliqueGetSignersTest {
private CliqueGetSigners method; private CliqueGetSigners method;
private BlockHeader blockHeader; private BlockHeader blockHeader;
private List<Address> validators; private NavigableSet<Address> validators;
private List<String> validatorAsStrings; private List<String> validatorAsStrings;
@Mock private BlockchainQueries blockchainQueries; @Mock private BlockchainQueries blockchainQueries;
@ -69,10 +71,11 @@ public class CliqueGetSignersTest {
blockHeader = blockHeaderTestFixture.extraData(bufferToInject).buildHeader(); blockHeader = blockHeaderTestFixture.extraData(bufferToInject).buildHeader();
validators = validators =
asList( new TreeSet<>(
fromHexString("0x42eb768f2244c8811c63729a21a3569731535f06"), asList(
fromHexString("0x7ffc57839b00206d1ad20c69a1981b489f772031"), fromHexString("0x42eb768f2244c8811c63729a21a3569731535f06"),
fromHexString("0xb279182d99e65703f0076e4812653aab85fca0f0")); fromHexString("0x7ffc57839b00206d1ad20c69a1981b489f772031"),
fromHexString("0xb279182d99e65703f0076e4812653aab85fca0f0")));
validatorAsStrings = validators.stream().map(Object::toString).collect(toList()); validatorAsStrings = validators.stream().map(Object::toString).collect(toList());
} }

@ -14,10 +14,10 @@ package tech.pegasys.pantheon.consensus.common;
import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.Address;
import java.util.Collection; import java.util.NavigableSet;
public interface ValidatorProvider { public interface ValidatorProvider {
// Returns the current list of validators // Returns the current list of validators
Collection<Address> getCurrentValidators(); NavigableSet<Address> getCurrentValidators();
} }

@ -20,9 +20,9 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NavigableSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -30,7 +30,7 @@ import com.google.common.collect.Maps;
/** Tracks the current list of validators and votes to add or drop validators. */ /** Tracks the current list of validators and votes to add or drop validators. */
public class VoteTally implements ValidatorProvider { public class VoteTally implements ValidatorProvider {
private final SortedSet<Address> currentValidators; private final NavigableSet<Address> currentValidators;
private final Map<Address, Set<Address>> addVotesBySubject; private final Map<Address, Set<Address>> addVotesBySubject;
private final Map<Address, Set<Address>> removeVotesBySubject; private final Map<Address, Set<Address>> removeVotesBySubject;
@ -108,7 +108,7 @@ public class VoteTally implements ValidatorProvider {
} }
@Override @Override
public Collection<Address> getCurrentValidators() { public NavigableSet<Address> getCurrentValidators() {
return currentValidators; return currentValidators;
} }

@ -32,6 +32,8 @@ import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List; import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.junit.Before; import org.junit.Before;
@ -42,7 +44,7 @@ import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class IbftNetworkPeersTest { public class IbftNetworkPeersTest {
private final List<Address> validators = Lists.newArrayList(); private final NavigableSet<Address> validators = new TreeSet<>();
private final List<PublicKey> publicKeys = Lists.newArrayList(); private final List<PublicKey> publicKeys = Lists.newArrayList();
private final List<PeerConnection> peerConnections = Lists.newArrayList(); private final List<PeerConnection> peerConnections = Lists.newArrayList();

Loading…
Cancel
Save