Clique Miner Executor added (#15)

This commit adds the clique specific implementation of the Miner
executor.

It is responsible for starting a clique mining operation when
requested. It also supplies the functionality to blend vanity data
with validator data etc. in the extra data field.
tmohay 6 years ago committed by GitHub
parent d2641d472e
commit 39c782320b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 97
      consensus/clique/src/main/java/net/consensys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutor.java
  2. 119
      consensus/clique/src/test/java/net/consensys/pantheon/consensus/clique/blockcreation/CliqueMinerExecutorTest.java

@ -0,0 +1,97 @@
package net.consensys.pantheon.consensus.clique.blockcreation;
import net.consensys.pantheon.consensus.clique.CliqueContext;
import net.consensys.pantheon.consensus.clique.CliqueExtraData;
import net.consensys.pantheon.consensus.common.EpochManager;
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.blockcreation.AbstractBlockScheduler;
import net.consensys.pantheon.ethereum.blockcreation.AbstractMinerExecutor;
import net.consensys.pantheon.ethereum.blockcreation.MiningCoordinator.MinedBlockObserver;
import net.consensys.pantheon.ethereum.blockcreation.MiningParameters;
import net.consensys.pantheon.ethereum.core.Address;
import net.consensys.pantheon.ethereum.core.BlockHeader;
import net.consensys.pantheon.ethereum.core.PendingTransactions;
import net.consensys.pantheon.ethereum.core.Util;
import net.consensys.pantheon.ethereum.mainnet.ProtocolSchedule;
import net.consensys.pantheon.util.Subscribers;
import net.consensys.pantheon.util.bytes.BytesValue;
import java.util.List;
import java.util.concurrent.ExecutorService;
import com.google.common.collect.Lists;
public class CliqueMinerExecutor extends AbstractMinerExecutor<CliqueContext, CliqueBlockMiner> {
private final Address localAddress;
private final KeyPair nodeKeys;
private final EpochManager epochManager;
private volatile BytesValue vanityData;
public CliqueMinerExecutor(
final ProtocolContext<CliqueContext> protocolContext,
final ExecutorService executorService,
final ProtocolSchedule<CliqueContext> protocolSchedule,
final PendingTransactions pendingTransactions,
final KeyPair nodeKeys,
final MiningParameters miningParams,
final AbstractBlockScheduler blockScheduler,
final EpochManager epochManager) {
super(
protocolContext,
executorService,
protocolSchedule,
pendingTransactions,
miningParams,
blockScheduler);
this.nodeKeys = nodeKeys;
this.localAddress = Util.publicKeyToAddress(nodeKeys.getPublicKey());
this.epochManager = epochManager;
this.vanityData = miningParams.getExtraData();
}
@Override
public CliqueBlockMiner startAsyncMining(
final Subscribers<MinedBlockObserver> observers, final BlockHeader parentHeader) {
CliqueBlockCreator blockCreator =
new CliqueBlockCreator(
localAddress, // TOOD(tmm): This can be removed (used for voting not coinbase).
this::calculateExtraData,
pendingTransactions,
protocolContext,
protocolSchedule,
(gasLimit) -> gasLimit,
nodeKeys,
minTransactionGasPrice,
parentHeader);
CliqueBlockMiner currentRunningMiner =
new CliqueBlockMiner(
blockCreator,
protocolSchedule,
protocolContext,
observers,
blockScheduler,
parentHeader,
localAddress);
executorService.execute(currentRunningMiner);
return currentRunningMiner;
}
public BytesValue calculateExtraData(final BlockHeader parentHeader) {
List<Address> validators = Lists.newArrayList();
// Building ON TOP of canonical head, if the next block is epoch, include validators.
if (epochManager.isEpochBlock(parentHeader.getNumber() + 1)) {
VoteTally voteTally =
protocolContext.getConsensusState().getVoteTallyCache().getVoteTallyAtBlock(parentHeader);
validators.addAll(voteTally.getCurrentValidators());
}
CliqueExtraData extraData = new CliqueExtraData(vanityData, null, validators);
return extraData.encode();
}
}

@ -0,0 +1,119 @@
package net.consensys.pantheon.consensus.clique.blockcreation;
import static org.assertj.core.api.Assertions.assertThat;
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.CliqueExtraData;
import net.consensys.pantheon.consensus.clique.CliqueProtocolSchedule;
import net.consensys.pantheon.consensus.clique.VoteTallyCache;
import net.consensys.pantheon.consensus.common.EpochManager;
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.blockcreation.MiningParameters;
import net.consensys.pantheon.ethereum.core.Address;
import net.consensys.pantheon.ethereum.core.AddressHelpers;
import net.consensys.pantheon.ethereum.core.BlockHeader;
import net.consensys.pantheon.ethereum.core.BlockHeaderTestFixture;
import net.consensys.pantheon.ethereum.core.PendingTransactions;
import net.consensys.pantheon.ethereum.core.Util;
import net.consensys.pantheon.ethereum.core.Wei;
import net.consensys.pantheon.util.bytes.BytesValue;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executors;
import com.google.common.collect.Lists;
import io.vertx.core.json.JsonObject;
import org.junit.Before;
import org.junit.Test;
public class CliqueMinerExecutorTest {
private final KeyPair proposerKeyPair = KeyPair.generate();
private Address localAddress;
private final List<Address> validatorList = Lists.newArrayList();
private ProtocolContext<CliqueContext> cliqueProtocolContext;
private BlockHeaderTestFixture blockHeaderBuilder;
@Before
public void setup() {
localAddress = Util.publicKeyToAddress(proposerKeyPair.getPublicKey());
validatorList.add(localAddress);
validatorList.add(AddressHelpers.calculateAddressWithRespectTo(localAddress, 1));
validatorList.add(AddressHelpers.calculateAddressWithRespectTo(localAddress, 2));
validatorList.add(AddressHelpers.calculateAddressWithRespectTo(localAddress, 3));
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<>(null, null, cliqueContext);
blockHeaderBuilder = new BlockHeaderTestFixture();
}
@Test
public void extraDataCreatedOnEpochBlocksContainsValidators() {
byte[] vanityData = new byte[32];
new Random().nextBytes(vanityData);
final BytesValue wrappedVanityData = BytesValue.wrap(vanityData);
final int EPOCH_LENGTH = 10;
final CliqueMinerExecutor executor =
new CliqueMinerExecutor(
cliqueProtocolContext,
Executors.newSingleThreadExecutor(),
CliqueProtocolSchedule.create(new JsonObject(), proposerKeyPair),
new PendingTransactions(1),
proposerKeyPair,
new MiningParameters(AddressHelpers.ofValue(1), Wei.ZERO, wrappedVanityData, false),
mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH));
// NOTE: Passing in the *parent* block, so must be 1 less than EPOCH
final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH - 1).buildHeader();
final BytesValue extraDataBytes = executor.calculateExtraData(header);
final CliqueExtraData cliqueExtraData = CliqueExtraData.decode(extraDataBytes);
assertThat(cliqueExtraData.getVanityData()).isEqualTo(wrappedVanityData);
assertThat(cliqueExtraData.getValidators())
.containsExactly(validatorList.toArray(new Address[0]));
}
@Test
public void extraDataForNonEpochBlocksDoesNotContainValidaors() {
byte[] vanityData = new byte[32];
new Random().nextBytes(vanityData);
final BytesValue wrappedVanityData = BytesValue.wrap(vanityData);
final int EPOCH_LENGTH = 10;
final CliqueMinerExecutor executor =
new CliqueMinerExecutor(
cliqueProtocolContext,
Executors.newSingleThreadExecutor(),
CliqueProtocolSchedule.create(new JsonObject(), proposerKeyPair),
new PendingTransactions(1),
proposerKeyPair,
new MiningParameters(AddressHelpers.ofValue(1), Wei.ZERO, wrappedVanityData, false),
mock(CliqueBlockScheduler.class),
new EpochManager(EPOCH_LENGTH));
// Parent block was epoch, so the next block should contain no validators.
final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH).buildHeader();
final BytesValue extraDataBytes = executor.calculateExtraData(header);
final CliqueExtraData cliqueExtraData = CliqueExtraData.decode(extraDataBytes);
assertThat(cliqueExtraData.getVanityData()).isEqualTo(wrappedVanityData);
assertThat(cliqueExtraData.getValidators()).isEqualTo(Lists.newArrayList());
}
}
Loading…
Cancel
Save