Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Ibft block mining #169

Merged
merged 2 commits into from
Nov 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ public Optional<ValidatorVote> extractVoteFromHeader(final BlockHeader header) {
return Optional.empty();
}

@Override
public BlockHeaderBuilder insertVoteToHeaderBuilder(
public static BlockHeaderBuilder insertVoteToHeaderBuilder(
final BlockHeaderBuilder builder, final Optional<ValidatorVote> vote) {
if (vote.isPresent()) {
final ValidatorVote voteToCast = vote.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
public class CliqueBlockCreator extends AbstractBlockCreator<CliqueContext> {

private final KeyPair nodeKeys;
private static final CliqueVotingBlockInterface votingInterface =
new CliqueVotingBlockInterface();

public CliqueBlockCreator(
final Address coinbase,
Expand Down Expand Up @@ -93,9 +91,11 @@ protected BlockHeader createFinalBlockHeader(final SealableBlockHeader sealableB
cliqueContext
.getVoteProposer()
.getVote(Util.publicKeyToAddress(nodeKeys.getPublicKey()), voteTally);
votingInterface.insertVoteToHeaderBuilder(builder, vote);
final BlockHeaderBuilder builderIncludingProposedVotes =
CliqueVotingBlockInterface.insertVoteToHeaderBuilder(builder, vote);

final CliqueExtraData sealedExtraData = constructSignedExtraData(builder.buildBlockHeader());
final CliqueExtraData sealedExtraData =
constructSignedExtraData(builderIncludingProposedVotes.buildBlockHeader());

// Replace the extraData in the BlockHeaderBuilder, and return header.
return builder.extraData(sealedExtraData.encode()).buildBlockHeader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import tech.pegasys.pantheon.consensus.clique.CliqueContext;
import tech.pegasys.pantheon.consensus.clique.CliqueExtraData;
import tech.pegasys.pantheon.consensus.common.ConsensusHelpers;
import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
Expand All @@ -29,7 +30,6 @@
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.util.Subscribers;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues;

import java.util.List;
import java.util.concurrent.ExecutorService;
Expand Down Expand Up @@ -96,7 +96,8 @@ public CliqueBlockMiner startAsyncMining(
public BytesValue calculateExtraData(final BlockHeader parentHeader) {
final List<Address> validators = Lists.newArrayList();

final BytesValue vanityDataToInsert = createCorrectlySizedVanityData();
final BytesValue vanityDataToInsert =
ConsensusHelpers.zeroLeftPad(vanityData, CliqueExtraData.EXTRA_VANITY_LENGTH);
// Building ON TOP of canonical head, if the next block is epoch, include validators.
if (epochManager.isEpochBlock(parentHeader.getNumber() + 1)) {
final VoteTally voteTally =
Expand All @@ -108,10 +109,4 @@ public BytesValue calculateExtraData(final BlockHeader parentHeader) {

return extraData.encode();
}

private BytesValue createCorrectlySizedVanityData() {
final int vanityPadding = Math.max(0, CliqueExtraData.EXTRA_VANITY_LENGTH - vanityData.size());
return BytesValues.concatenate(BytesValue.wrap(new byte[vanityPadding]), vanityData)
.slice(0, CliqueExtraData.EXTRA_VANITY_LENGTH);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void blendingAddVoteToHeaderResultsInHeaderWithNonceOfMaxLong() {
final ValidatorVote vote =
new ValidatorVote(ADD, AddressHelpers.ofValue(1), AddressHelpers.ofValue(2));
final BlockHeaderBuilder builderWithVote =
blockInterface.insertVoteToHeaderBuilder(builder, Optional.of(vote));
CliqueVotingBlockInterface.insertVoteToHeaderBuilder(builder, Optional.of(vote));

final BlockHeader header = builderWithVote.buildBlockHeader();

Expand All @@ -100,7 +100,7 @@ public void blendingDropVoteToHeaderResultsInHeaderWithNonceOfZero() {
final ValidatorVote vote =
new ValidatorVote(DROP, AddressHelpers.ofValue(1), AddressHelpers.ofValue(2));
final BlockHeaderBuilder builderWithVote =
blockInterface.insertVoteToHeaderBuilder(builder, Optional.of(vote));
CliqueVotingBlockInterface.insertVoteToHeaderBuilder(builder, Optional.of(vote));

final BlockHeader header = builderWithVote.buildBlockHeader();

Expand All @@ -111,7 +111,7 @@ public void blendingDropVoteToHeaderResultsInHeaderWithNonceOfZero() {
@Test
public void nonVoteBlendedIntoHeaderResultsInACoinbaseOfZero() {
final BlockHeaderBuilder builderWithVote =
blockInterface.insertVoteToHeaderBuilder(builder, Optional.empty());
CliqueVotingBlockInterface.insertVoteToHeaderBuilder(builder, Optional.empty());

final BlockHeader header = builderWithVote.buildBlockHeader();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.common;

import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues;

public class ConsensusHelpers {

public static BytesValue zeroLeftPad(final BytesValue input, final int requiredLength) {
final int paddingByteCount = Math.max(0, requiredLength - input.size());
return BytesValues.concatenate(BytesValue.wrap(new byte[paddingByteCount]), input)
.slice(0, requiredLength);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderBuilder;

import java.util.List;
import java.util.Optional;
Expand All @@ -23,8 +22,5 @@ public interface VoteBlockInterface {

Optional<ValidatorVote> extractVoteFromHeader(final BlockHeader header);

BlockHeaderBuilder insertVoteToHeaderBuilder(
final BlockHeaderBuilder builder, final Optional<ValidatorVote> vote);

List<Address> validatorsInBlock(final BlockHeader header);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@
*/
package tech.pegasys.pantheon.consensus.ibft;

import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory;

/** Stateful evaluator for ibft events */
public class IbftStateMachine {

private final IbftBlockCreatorFactory blockCreatorFactory;

public IbftStateMachine(final IbftBlockCreatorFactory blockCreatorFactory) {
this.blockCreatorFactory = blockCreatorFactory;
}

/**
* Attempt to consume the event and update the maintained state
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import tech.pegasys.pantheon.consensus.common.VoteType;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderBuilder;

import java.util.List;
import java.util.Optional;
Expand All @@ -40,29 +39,6 @@ public Optional<ValidatorVote> extractVoteFromHeader(final BlockHeader header) {
return Optional.empty();
}

@Override
public BlockHeaderBuilder insertVoteToHeaderBuilder(
final BlockHeaderBuilder builder, final Optional<ValidatorVote> vote) {
final BlockHeader header = builder.buildBlockHeader();
final IbftExtraData extraData = IbftExtraData.decode(header.getExtraData());

Optional<Vote> headerVote = Optional.empty();

if (vote.isPresent()) {
final ValidatorVote voteToCast = vote.get();
headerVote = Optional.of(new Vote(voteToCast.getRecipient(), voteToCast.getVotePolarity()));
}

final IbftExtraData includingVote =
new IbftExtraData(
extraData.getVanityData(),
extraData.getSeals(),
headerVote,
extraData.getRound(),
extraData.getValidators());
return BlockHeaderBuilder.fromHeader(header).extraData(includingVote.encode());
}

@Override
public List<Address> validatorsInBlock(final BlockHeader header) {
final IbftExtraData ibftExtraData = IbftExtraData.decode(header.getExtraData());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,55 @@
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.blockcreation.AbstractBlockCreator;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.BlockHashFunction;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderBuilder;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.core.PendingTransactions;
import tech.pegasys.pantheon.ethereum.core.SealableBlockHeader;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ScheduleBasedBlockHashFunction;

import java.util.function.Function;

// TODO: Just a placeholder. Implementation is required.
// This class is responsible for creating a block without committer seals (basically it was just
// too hard to coordinate with the state machine).
public class IbftBlockCreator extends AbstractBlockCreator<IbftContext> {
public IbftBlockCreator(
final Address coinbase,
final Address localAddress,
final ExtraDataCalculator extraDataCalculator,
final PendingTransactions pendingTransactions,
final ProtocolContext<IbftContext> protocolContext,
final ProtocolSchedule<IbftContext> protocolSchedule,
final Function<Long, Long> gasLimitCalculator,
final Wei minTransactionGasPrice,
final Address miningBeneficiary,
final BlockHeader parentHeader) {
super(
coinbase,
localAddress,
extraDataCalculator,
pendingTransactions,
protocolContext,
protocolSchedule,
gasLimitCalculator,
minTransactionGasPrice,
miningBeneficiary,
localAddress,
parentHeader);
}

@Override
protected BlockHeader createFinalBlockHeader(final SealableBlockHeader sealableBlockHeader) {
return null;

final BlockHashFunction blockHashFunction =
ScheduleBasedBlockHashFunction.create(protocolSchedule);

final BlockHeaderBuilder builder =
BlockHeaderBuilder.create()
.populateFrom(sealableBlockHeader)
.mixHash(Hash.ZERO)
.nonce(0L)
.blockHashFunction(blockHashFunction);

return builder.buildBlockHeader();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.blockcreation;

import tech.pegasys.pantheon.consensus.common.ConsensusHelpers;
import tech.pegasys.pantheon.consensus.common.ValidatorVote;
import tech.pegasys.pantheon.consensus.common.VoteTally;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.consensus.ibft.IbftExtraData;
import tech.pegasys.pantheon.consensus.ibft.Vote;
import tech.pegasys.pantheon.ethereum.ProtocolContext;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.MiningParameters;
import tech.pegasys.pantheon.ethereum.core.PendingTransactions;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.util.bytes.BytesValue;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

public class IbftBlockCreatorFactory {

private final Function<Long, Long> gasLimitCalculator;
private final PendingTransactions pendingTransactions;
protected final ProtocolContext<IbftContext> protocolContext;
protected final ProtocolSchedule<IbftContext> protocolSchedule;
private final Address localAddress;

private volatile BytesValue vanityData;
private volatile Wei minTransactionGasPrice;

public IbftBlockCreatorFactory(
final Function<Long, Long> gasLimitCalculator,
final PendingTransactions pendingTransactions,
final ProtocolContext<IbftContext> protocolContext,
final ProtocolSchedule<IbftContext> protocolSchedule,
final MiningParameters miningParams,
final Address localAddress) {
this.gasLimitCalculator = gasLimitCalculator;
this.pendingTransactions = pendingTransactions;
this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule;
this.localAddress = localAddress;
this.minTransactionGasPrice = miningParams.getMinTransactionGasPrice();
this.vanityData = miningParams.getExtraData();
}

public IbftBlockCreator create(final BlockHeader parentHeader, final int round) {
return new IbftBlockCreator(
localAddress,
ph -> createExtraData(round),
pendingTransactions,
protocolContext,
protocolSchedule,
gasLimitCalculator,
minTransactionGasPrice,
parentHeader);
}

public void setExtraData(final BytesValue extraData) {
this.vanityData = extraData.copy();
}

public void setMinTransactionGasPrice(final Wei minTransactionGasPrice) {
this.minTransactionGasPrice = minTransactionGasPrice.copy();
}

public Wei getMinTransactionGasPrice() {
return minTransactionGasPrice;
}

public BytesValue createExtraData(final int round) {
final VoteTally voteTally = protocolContext.getConsensusState().getVoteTally();
final Optional<ValidatorVote> proposal =
protocolContext.getConsensusState().getVoteProposer().getVote(localAddress, voteTally);

final List<Address> validators = new ArrayList<>(voteTally.getCurrentValidators());

final IbftExtraData extraData =
new IbftExtraData(
ConsensusHelpers.zeroLeftPad(vanityData, IbftExtraData.EXTRA_VANITY_LENGTH),
Collections.emptyList(),
toVote(proposal),
round,
validators);

return extraData.encode();
}

private static Optional<Vote> toVote(final Optional<ValidatorVote> input) {
return input
.map(v -> Optional.of(new Vote(v.getRecipient(), v.getVotePolarity())))
.orElse(Optional.empty());
}
}
Loading