diff --git a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockInterface.java b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockInterface.java index db6e46607c..72176e8cfc 100644 --- a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockInterface.java +++ b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockInterface.java @@ -57,17 +57,18 @@ public Optional extractVoteFromHeader(final BlockHeader header) { return Optional.empty(); } - public static BlockHeaderBuilder insertVoteToHeaderBuilder( + public static BlockHeaderBuilder createHeaderBuilderWithVoteHeaders( final BlockHeaderBuilder builder, final Optional vote) { + final BlockHeaderBuilder voteHeaderBuilder = BlockHeaderBuilder.fromBuilder(builder); if (vote.isPresent()) { final ValidatorVote voteToCast = vote.get(); - builder.nonce(voteToValue.get(voteToCast.getVotePolarity())); - builder.coinbase(voteToCast.getRecipient()); + voteHeaderBuilder.nonce(voteToValue.get(voteToCast.getVotePolarity())); + voteHeaderBuilder.coinbase(voteToCast.getRecipient()); } else { - builder.nonce(voteToValue.get(VoteType.DROP)); - builder.coinbase(NO_VOTE_SUBJECT); + voteHeaderBuilder.nonce(voteToValue.get(VoteType.DROP)); + voteHeaderBuilder.coinbase(NO_VOTE_SUBJECT); } - return builder; + return voteHeaderBuilder; } @Override diff --git a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreator.java b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreator.java index c1903486c3..95c8ef4bf4 100644 --- a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreator.java +++ b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreator.java @@ -16,6 +16,7 @@ import tech.pegasys.pantheon.consensus.clique.CliqueBlockInterface; import tech.pegasys.pantheon.consensus.clique.CliqueContext; import tech.pegasys.pantheon.consensus.clique.CliqueExtraData; +import tech.pegasys.pantheon.consensus.common.EpochManager; import tech.pegasys.pantheon.consensus.common.ValidatorVote; import tech.pegasys.pantheon.consensus.common.VoteTally; import tech.pegasys.pantheon.crypto.SECP256K1; @@ -40,6 +41,7 @@ public class CliqueBlockCreator extends AbstractBlockCreator { private final KeyPair nodeKeys; + private final EpochManager epochManager; public CliqueBlockCreator( final Address coinbase, @@ -50,7 +52,8 @@ public CliqueBlockCreator( final Function gasLimitCalculator, final KeyPair nodeKeys, final Wei minTransactionGasPrice, - final BlockHeader parentHeader) { + final BlockHeader parentHeader, + final EpochManager epochManager) { super( coinbase, extraDataCalculator, @@ -62,6 +65,7 @@ public CliqueBlockCreator( Util.publicKeyToAddress(nodeKeys.getPublicKey()), parentHeader); this.nodeKeys = nodeKeys; + this.epochManager = epochManager; } /** @@ -74,7 +78,6 @@ public CliqueBlockCreator( */ @Override protected BlockHeader createFinalBlockHeader(final SealableBlockHeader sealableBlockHeader) { - final BlockHashFunction blockHashFunction = ScheduleBasedBlockHashFunction.create(protocolSchedule); @@ -84,22 +87,28 @@ protected BlockHeader createFinalBlockHeader(final SealableBlockHeader sealableB .mixHash(Hash.ZERO) .blockHashFunction(blockHashFunction); - final CliqueContext cliqueContext = protocolContext.getConsensusState(); - final VoteTally voteTally = - cliqueContext.getVoteTallyCache().getVoteTallyAfterBlock(parentHeader); - - final Optional vote = - cliqueContext - .getVoteProposer() - .getVote(Util.publicKeyToAddress(nodeKeys.getPublicKey()), voteTally); + final Optional vote = determineCliqueVote(sealableBlockHeader); final BlockHeaderBuilder builderIncludingProposedVotes = - CliqueBlockInterface.insertVoteToHeaderBuilder(builder, vote); - + CliqueBlockInterface.createHeaderBuilderWithVoteHeaders(builder, vote); final CliqueExtraData sealedExtraData = constructSignedExtraData(builderIncludingProposedVotes.buildBlockHeader()); // Replace the extraData in the BlockHeaderBuilder, and return header. - return builder.extraData(sealedExtraData.encode()).buildBlockHeader(); + return builderIncludingProposedVotes.extraData(sealedExtraData.encode()).buildBlockHeader(); + } + + private Optional determineCliqueVote( + final SealableBlockHeader sealableBlockHeader) { + if (epochManager.isEpochBlock(sealableBlockHeader.getNumber())) { + return Optional.empty(); + } else { + final CliqueContext cliqueContext = protocolContext.getConsensusState(); + final VoteTally voteTally = + cliqueContext.getVoteTallyCache().getVoteTallyAfterBlock(parentHeader); + return cliqueContext + .getVoteProposer() + .getVote(Util.publicKeyToAddress(nodeKeys.getPublicKey()), voteTally); + } } /** diff --git a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java index 7de025bdcb..0472b52720 100644 --- a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java +++ b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java @@ -78,7 +78,8 @@ public CliqueBlockMiner startAsyncMining( (gasLimit) -> gasLimit, nodeKeys, minTransactionGasPrice, - parentHeader); + parentHeader, + epochManager); final CliqueBlockMiner currentRunningMiner = new CliqueBlockMiner( diff --git a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockInterfaceTest.java b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockInterfaceTest.java index 2aa090dc3a..6ccbc2559c 100644 --- a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockInterfaceTest.java +++ b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueBlockInterfaceTest.java @@ -87,7 +87,7 @@ public void blendingAddVoteToHeaderResultsInHeaderWithNonceOfMaxLong() { final ValidatorVote vote = new ValidatorVote(ADD, AddressHelpers.ofValue(1), AddressHelpers.ofValue(2)); final BlockHeaderBuilder builderWithVote = - CliqueBlockInterface.insertVoteToHeaderBuilder(builder, Optional.of(vote)); + CliqueBlockInterface.createHeaderBuilderWithVoteHeaders(builder, Optional.of(vote)); final BlockHeader header = builderWithVote.buildBlockHeader(); @@ -101,7 +101,7 @@ public void blendingDropVoteToHeaderResultsInHeaderWithNonceOfZero() { final ValidatorVote vote = new ValidatorVote(DROP, AddressHelpers.ofValue(1), AddressHelpers.ofValue(2)); final BlockHeaderBuilder builderWithVote = - CliqueBlockInterface.insertVoteToHeaderBuilder(builder, Optional.of(vote)); + CliqueBlockInterface.createHeaderBuilderWithVoteHeaders(builder, Optional.of(vote)); final BlockHeader header = builderWithVote.buildBlockHeader(); @@ -112,7 +112,7 @@ public void blendingDropVoteToHeaderResultsInHeaderWithNonceOfZero() { @Test public void nonVoteBlendedIntoHeaderResultsInACoinbaseOfZero() { final BlockHeaderBuilder builderWithVote = - CliqueBlockInterface.insertVoteToHeaderBuilder(builder, Optional.empty()); + CliqueBlockInterface.createHeaderBuilderWithVoteHeaders(builder, Optional.empty()); final BlockHeader header = builderWithVote.buildBlockHeader(); diff --git a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java index 988d8c23c3..320350f227 100644 --- a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java +++ b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/blockcreation/CliqueBlockCreatorTest.java @@ -27,6 +27,7 @@ import tech.pegasys.pantheon.consensus.clique.CliqueProtocolSchedule; import tech.pegasys.pantheon.consensus.clique.TestHelpers; import tech.pegasys.pantheon.consensus.clique.VoteTallyCache; +import tech.pegasys.pantheon.consensus.common.EpochManager; import tech.pegasys.pantheon.consensus.common.VoteProposer; import tech.pegasys.pantheon.consensus.common.VoteTally; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; @@ -64,6 +65,7 @@ public class CliqueBlockCreatorTest { private MutableBlockchain blockchain; private ProtocolContext protocolContext; private VoteProposer voteProposer; + private EpochManager epochManager; @Before public void setup() { @@ -83,6 +85,7 @@ public void setup() { GenesisState.fromConfig(GenesisConfigFile.mainnet(), protocolSchedule).getBlock(); blockchain = createInMemoryBlockchain(genesis); protocolContext = new ProtocolContext<>(blockchain, stateArchive, cliqueContext); + epochManager = new EpochManager(10); // Add a block above the genesis final BlockHeaderTestFixture headerTestFixture = new BlockHeaderTestFixture(); @@ -112,7 +115,8 @@ public void proposerAddressCanBeExtractFromAConstructedBlock() { gasLimit -> gasLimit, proposerKeyPair, Wei.ZERO, - blockchain.getChainHeadHeader()); + blockchain.getChainHeadHeader(), + epochManager); final Block createdBlock = blockCreator.createBlock(5L); @@ -138,7 +142,8 @@ public void insertsValidVoteIntoConstructedBlock() { gasLimit -> gasLimit, proposerKeyPair, Wei.ZERO, - blockchain.getChainHeadHeader()); + blockchain.getChainHeadHeader(), + epochManager); final Block createdBlock = blockCreator.createBlock(0L); assertThat(createdBlock.getHeader().getNonce()).isEqualTo(CliqueBlockInterface.ADD_NONCE); @@ -163,7 +168,37 @@ public void insertsNoVoteWhenAuthInValidators() { gasLimit -> gasLimit, proposerKeyPair, Wei.ZERO, - blockchain.getChainHeadHeader()); + blockchain.getChainHeadHeader(), + epochManager); + + final Block createdBlock = blockCreator.createBlock(0L); + assertThat(createdBlock.getHeader().getNonce()).isEqualTo(CliqueBlockInterface.DROP_NONCE); + assertThat(createdBlock.getHeader().getCoinbase()).isEqualTo(Address.fromHexString("0")); + } + + @Test + public void insertsNoVoteWhenAtEpoch() { + // ensure that the next block is epoch + epochManager = new EpochManager(1); + + final CliqueExtraData extraData = + new CliqueExtraData(BytesValue.wrap(new byte[32]), null, validatorList); + final Address a1 = Address.fromHexString("5"); + voteProposer.auth(a1); + final Address coinbase = AddressHelpers.ofValue(1); + + final CliqueBlockCreator blockCreator = + new CliqueBlockCreator( + coinbase, + parent -> extraData.encode(), + new PendingTransactions(5), + protocolContext, + protocolSchedule, + gasLimit -> gasLimit, + proposerKeyPair, + Wei.ZERO, + blockchain.getChainHeadHeader(), + epochManager); final Block createdBlock = blockCreator.createBlock(0L); assertThat(createdBlock.getHeader().getNonce()).isEqualTo(CliqueBlockInterface.DROP_NONCE); diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/BlockHeaderBuilder.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/BlockHeaderBuilder.java index c114e7f462..010e442b1b 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/BlockHeaderBuilder.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/BlockHeaderBuilder.java @@ -82,6 +82,28 @@ public static BlockHeaderBuilder fromHeader(final BlockHeader header) { .nonce(header.getNonce()); } + public static BlockHeaderBuilder fromBuilder(final BlockHeaderBuilder fromBuilder) { + BlockHeaderBuilder toBuilder = + create() + .parentHash(fromBuilder.parentHash) + .ommersHash(fromBuilder.ommersHash) + .coinbase(fromBuilder.coinbase) + .stateRoot(fromBuilder.stateRoot) + .transactionsRoot(fromBuilder.transactionsRoot) + .receiptsRoot(fromBuilder.receiptsRoot) + .logsBloom(fromBuilder.logsBloom) + .difficulty(fromBuilder.difficulty) + .number(fromBuilder.number) + .gasLimit(fromBuilder.gasLimit) + .gasUsed(fromBuilder.gasUsed) + .timestamp(fromBuilder.timestamp) + .extraData(fromBuilder.extraData) + .mixHash(fromBuilder.mixHash) + .blockHashFunction(fromBuilder.blockHashFunction); + toBuilder.nonce = fromBuilder.nonce; + return toBuilder; + } + public BlockHeader buildBlockHeader() { validateBlockHeader();