mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 15:37:54 -05:00
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. Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user