Skip to content

Commit

Permalink
Withdrawals processor (hyperledger#4917)
Browse files Browse the repository at this point in the history
Signed-off-by: Jason Frame <jason.frame@consensys.net>
  • Loading branch information
jframe authored Jan 16, 2023
1 parent 8a886c8 commit e89b773
Show file tree
Hide file tree
Showing 33 changed files with 796 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.mainnet.HeaderBasedProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
Expand All @@ -34,14 +35,16 @@ public MergeBlockProcessor(
final Wei blockReward,
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
final boolean skipZeroBlockRewards,
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters,
final HeaderBasedProtocolSchedule protocolSchedule) {
super(
transactionProcessor,
transactionReceiptFactory,
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
goQuorumPrivacyParameters);
goQuorumPrivacyParameters,
protocolSchedule);
this.mergeContext = PostMergeContext.get();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;

Expand Down Expand Up @@ -62,10 +63,12 @@ public class MergeBlockCreator extends AbstractBlockCreator {
public BlockCreationResult createBlock(
final Optional<List<Transaction>> maybeTransactions,
final Bytes32 random,
final long timestamp) {
final long timestamp,
final Optional<List<Withdrawal>> withdrawals) {
return createBlock(
maybeTransactions,
Optional.of(Collections.emptyList()),
withdrawals,
Optional.of(random),
timestamp,
false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext;
import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BadChainListener;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
Expand Down Expand Up @@ -209,7 +210,8 @@ public PayloadIdentifier preparePayload(
final BlockHeader parentHeader,
final Long timestamp,
final Bytes32 prevRandao,
final Address feeRecipient) {
final Address feeRecipient,
final Optional<List<Withdrawal>> withdrawals) {

// we assume that preparePayload is always called sequentially, since the RPC Engine calls
// are sequential, if this assumption changes then more synchronization should be added to
Expand All @@ -234,7 +236,7 @@ public PayloadIdentifier preparePayload(
// put the empty block in first
final Block emptyBlock =
mergeBlockCreator
.createBlock(Optional.of(Collections.emptyList()), prevRandao, timestamp)
.createBlock(Optional.of(Collections.emptyList()), prevRandao, timestamp, withdrawals)
.getBlock();

BlockProcessingResult result = validateBlock(emptyBlock);
Expand All @@ -256,7 +258,7 @@ public PayloadIdentifier preparePayload(
}
}

tryToBuildBetterBlock(timestamp, prevRandao, payloadIdentifier, mergeBlockCreator);
tryToBuildBetterBlock(timestamp, prevRandao, payloadIdentifier, mergeBlockCreator, withdrawals);

return payloadIdentifier;
}
Expand All @@ -276,10 +278,11 @@ private void tryToBuildBetterBlock(
final Long timestamp,
final Bytes32 random,
final PayloadIdentifier payloadIdentifier,
final MergeBlockCreator mergeBlockCreator) {
final MergeBlockCreator mergeBlockCreator,
final Optional<List<Withdrawal>> withdrawals) {

final Supplier<BlockCreationResult> blockCreator =
() -> mergeBlockCreator.createBlock(Optional.empty(), random, timestamp);
() -> mergeBlockCreator.createBlock(Optional.empty(), random, timestamp, withdrawals);

LOG.debug(
"Block creation started for payload id {}, remaining time is {}ms",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Withdrawal;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

Expand All @@ -34,7 +36,8 @@ PayloadIdentifier preparePayload(
final BlockHeader parentHeader,
final Long timestamp,
final Bytes32 prevRandao,
final Address feeRecipient);
final Address feeRecipient,
final Optional<List<Withdrawal>> withdrawals);

@Override
default boolean isCompatibleWithEngineApi() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Withdrawal;

import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -133,8 +134,10 @@ public PayloadIdentifier preparePayload(
final BlockHeader parentHeader,
final Long timestamp,
final Bytes32 prevRandao,
final Address feeRecipient) {
return mergeCoordinator.preparePayload(parentHeader, timestamp, prevRandao, feeRecipient);
final Address feeRecipient,
final Optional<List<Withdrawal>> withdrawals) {
return mergeCoordinator.preparePayload(
parentHeader, timestamp, prevRandao, feeRecipient, withdrawals);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.eth.sync.backwardsync.BackwardSyncContext;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.BaseFeePendingTransactionsSorter;
Expand All @@ -74,6 +75,7 @@
import org.hyperledger.besu.testutil.TestClock;

import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
Expand Down Expand Up @@ -111,6 +113,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
"ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f"));
private static final KeyPair KEYS1 =
new KeyPair(PRIVATE_KEY1, SIGNATURE_ALGORITHM.get().createPublicKey(PRIVATE_KEY1));
private static final Optional<List<Withdrawal>> EMPTY_WITHDRAWALS = Optional.empty();
@Mock MergeContext mergeContext;
@Mock BackwardSyncContext backwardSyncContext;

Expand Down Expand Up @@ -214,7 +217,8 @@ public void coinbaseShouldMatchSuggestedFeeRecipient() {
genesisState.getBlock().getHeader(),
System.currentTimeMillis() / 1000,
Bytes32.ZERO,
suggestedFeeRecipient);
suggestedFeeRecipient,
EMPTY_WITHDRAWALS);

ArgumentCaptor<BlockWithReceipts> blockWithReceipts =
ArgumentCaptor.forClass(BlockWithReceipts.class);
Expand Down Expand Up @@ -252,7 +256,7 @@ public void exceptionDuringBuildingBlockShouldNotBeInvalid()
.doThrow(new MerkleTrieException("missing leaf"))
.doCallRealMethod()
.when(beingSpiedOn)
.createBlock(any(), any(Bytes32.class), anyLong());
.createBlock(any(), any(Bytes32.class), anyLong(), eq(Optional.empty()));
return beingSpiedOn;
};

Expand Down Expand Up @@ -287,7 +291,8 @@ public void exceptionDuringBuildingBlockShouldNotBeInvalid()
genesisState.getBlock().getHeader(),
System.currentTimeMillis() / 1000,
Bytes32.random(),
suggestedFeeRecipient);
suggestedFeeRecipient,
Optional.empty());

verify(willThrow, never()).addBadBlock(any(), any());
blockCreationTask.get();
Expand Down Expand Up @@ -330,7 +335,8 @@ public void shouldContinueBuildingBlocksUntilFinalizeIsCalled()
genesisState.getBlock().getHeader(),
System.currentTimeMillis() / 1000,
Bytes32.ZERO,
suggestedFeeRecipient);
suggestedFeeRecipient,
Optional.empty());

blockCreationTask.get();

Expand Down Expand Up @@ -380,7 +386,8 @@ public void shouldRetryBlockCreationOnRecoverableError()
genesisState.getBlock().getHeader(),
System.currentTimeMillis() / 1000,
Bytes32.ZERO,
suggestedFeeRecipient);
suggestedFeeRecipient,
Optional.empty());

blockCreationTask.get();

Expand Down Expand Up @@ -413,7 +420,8 @@ public void shouldStopRetryBlockCreationIfTimeExpired() throws InterruptedExcept
genesisState.getBlock().getHeader(),
System.currentTimeMillis() / 1000,
Bytes32.ZERO,
suggestedFeeRecipient);
suggestedFeeRecipient,
Optional.empty());

try {
blockCreationTask.get();
Expand Down Expand Up @@ -454,7 +462,8 @@ public void shouldStopInProgressBlockCreationIfFinalizedIsCalled()
genesisState.getBlock().getHeader(),
System.currentTimeMillis() / 1000,
Bytes32.ZERO,
suggestedFeeRecipient);
suggestedFeeRecipient,
Optional.empty());

waitForBlockCreationInProgress.await();
coordinator.finalizeProposalById(payloadId);
Expand Down Expand Up @@ -496,13 +505,21 @@ public void shouldNotStartAnotherBlockCreationJobIfCalledAgainWithTheSamePayload

var payloadId1 =
coordinator.preparePayload(
genesisState.getBlock().getHeader(), timestamp, Bytes32.ZERO, suggestedFeeRecipient);
genesisState.getBlock().getHeader(),
timestamp,
Bytes32.ZERO,
suggestedFeeRecipient,
Optional.empty());

final CompletableFuture<Void> task1 = blockCreationTask;

var payloadId2 =
coordinator.preparePayload(
genesisState.getBlock().getHeader(), timestamp, Bytes32.ZERO, suggestedFeeRecipient);
genesisState.getBlock().getHeader(),
timestamp,
Bytes32.ZERO,
suggestedFeeRecipient,
Optional.empty());

assertThat(payloadId1).isEqualTo(payloadId2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,16 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
maybePayloadAttributes.ifPresentOrElse(
this::logPayload, () -> LOG.debug("Payload attributes are null"));

final Optional<List<Withdrawal>> withdrawals =
maybePayloadAttributes.flatMap(
payloadAttributes ->
Optional.ofNullable(payloadAttributes.getWithdrawals())
.map(
ws ->
ws.stream().map(WithdrawalParameter::toWithdrawal).collect(toList())));

if (maybePayloadAttributes.isPresent()
&& !isPayloadAttributesValid(maybePayloadAttributes.get(), newHead)) {
&& !isPayloadAttributesValid(maybePayloadAttributes.get(), withdrawals, newHead)) {
warnLambda(
LOG,
"Invalid payload attributes: {}",
Expand All @@ -162,7 +170,8 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
newHead,
payloadAttributes.getTimestamp(),
payloadAttributes.getPrevRandao(),
payloadAttributes.getSuggestedFeeRecipient()));
payloadAttributes.getSuggestedFeeRecipient(),
withdrawals));

payloadId.ifPresent(
pid ->
Expand All @@ -183,19 +192,19 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
}

private boolean isPayloadAttributesValid(
final EnginePayloadAttributesParameter payloadAttributes, final BlockHeader headBlockHeader) {
final EnginePayloadAttributesParameter payloadAttributes,
final Optional<List<Withdrawal>> withdrawals,
final BlockHeader headBlockHeader) {

final boolean newTimestampGreaterThanHead =
payloadAttributes.getTimestamp() > headBlockHeader.getTimestamp();
return newTimestampGreaterThanHead && isWithdrawalsValid(payloadAttributes);
return newTimestampGreaterThanHead && isWithdrawalsValid(payloadAttributes, withdrawals);
}

private boolean isWithdrawalsValid(final EnginePayloadAttributesParameter payloadAttributes) {
final List<Withdrawal> withdrawals =
Optional.ofNullable(payloadAttributes.getWithdrawals())
.map(ws -> ws.stream().map(WithdrawalParameter::toWithdrawal).collect(toList()))
.orElse(null);

private boolean isWithdrawalsValid(
final EnginePayloadAttributesParameter payloadAttributes,
final Optional<List<Withdrawal>> maybeWithdrawals) {
final List<Withdrawal> withdrawals = maybeWithdrawals.orElse(null);
return timestampSchedule
.getByTimestamp(payloadAttributes.getTimestamp())
.map(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ public class EthGetTransactionReceiptTest {
FeeMarket.legacy(),
null,
Optional.of(PoWHasher.ETHASH_LIGHT),
null);
null,
Optional.empty());
private final ProtocolSpec statusTransactionTypeSpec =
new ProtocolSpec(
"status",
Expand All @@ -138,7 +139,8 @@ public class EthGetTransactionReceiptTest {
FeeMarket.legacy(),
null,
Optional.of(PoWHasher.ETHASH_LIGHT),
null);
null,
Optional.empty());

@SuppressWarnings("unchecked")
private final ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class);
Expand Down
Loading

0 comments on commit e89b773

Please sign in to comment.