Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -32,6 +32,7 @@
import org.hyperledger.besu.plugin.services.MetricsSystem;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -74,12 +75,32 @@ private CompleteBlocksTask(
this.blocks =
headers.stream()
.filter(this::hasEmptyBody)
.collect(toMap(BlockHeader::getNumber, header -> new Block(header, BlockBody.empty())));
.collect(
toMap(
BlockHeader::getNumber,
header ->
new Block(
header,
new BlockBody(
Collections.emptyList(),
Collections.emptyList(),
isWithdrawalsEnabled(protocolSchedule, header)
? Optional.of(Collections.emptyList())
: Optional.empty()))));
Comment thread
gfukushima marked this conversation as resolved.
Outdated
}

private static boolean isWithdrawalsEnabled(
final ProtocolSchedule protocolSchedule, final BlockHeader header) {
return protocolSchedule.getByBlockHeader(header).getWithdrawalsProcessor().isPresent();
}

Comment thread
gfukushima marked this conversation as resolved.
private boolean hasEmptyBody(final BlockHeader header) {
return header.getOmmersHash().equals(Hash.EMPTY_LIST_HASH)
&& header.getTransactionsRoot().equals(Hash.EMPTY_TRIE_HASH);
&& header.getTransactionsRoot().equals(Hash.EMPTY_TRIE_HASH)
&& header
.getWithdrawalsRoot()
Comment thread
jframe marked this conversation as resolved.
.map(wsRoot -> wsRoot.equals(Hash.EMPTY_TRIE_HASH))
.orElse(true);
}

public static CompleteBlocksTask forHeaders(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,42 @@
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.GWei;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Withdrawal;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.ethtaskutils.RetryingMessageTaskTest;
import org.hyperledger.besu.ethereum.eth.manager.exceptions.MaxRetriesReachedException;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockBodiesMessage;
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsProcessor;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.apache.tuweni.units.bigints.UInt64;
import org.junit.Test;
import org.mockito.Mockito;

public class CompleteBlocksTaskTest extends RetryingMessageTaskTest<List<Block>> {

Expand Down Expand Up @@ -79,6 +95,115 @@ public void shouldCompleteWithoutPeersWhenAllBlocksAreEmpty() {
assertThat(task.run()).isCompletedWithValue(blocks);
}

@Test
public void shouldCreateCorrectEmptyBlockWhenWithdrawalsAreEnabled() {
Comment thread
jframe marked this conversation as resolved.
Outdated
final ProtocolSchedule mockProtocolSchedule = Mockito.mock(ProtocolSchedule.class);
final ProtocolSpec mockParisSpec = Mockito.mock(ProtocolSpec.class);
final ProtocolSpec mockShanghaiSpec = Mockito.mock(ProtocolSpec.class);
final WithdrawalsProcessor mockWithdrawalsProcessor = Mockito.mock(WithdrawalsProcessor.class);

final BlockHeader header1 =
new BlockHeaderTestFixture().number(1).withdrawalsRoot(null).buildHeader();
final BlockHeader header2 =
new BlockHeaderTestFixture().number(2).withdrawalsRoot(Hash.EMPTY_TRIE_HASH).buildHeader();

when(mockProtocolSchedule.getByBlockHeader((eq(header1)))).thenReturn(mockParisSpec);
when(mockParisSpec.getWithdrawalsProcessor()).thenReturn(Optional.empty());
when(mockProtocolSchedule.getByBlockHeader((eq(header2)))).thenReturn(mockShanghaiSpec);
when(mockShanghaiSpec.getWithdrawalsProcessor())
.thenReturn(Optional.of(mockWithdrawalsProcessor));

final Block block1 =
new Block(header1, new BlockBody(Collections.emptyList(), Collections.emptyList()));
final Block block2 =
new Block(
header2,
new BlockBody(
Collections.emptyList(),
Collections.emptyList(),
Optional.of(Collections.emptyList())));

final List<Block> expectedBlocks = asList(block1, block2);
final EthTask<List<Block>> task =
CompleteBlocksTask.forHeaders(
mockProtocolSchedule,
ethContext,
List.of(header1, header2),
maxRetries,
new NoOpMetricsSystem());
assertThat(task.run()).isCompletedWithValue(expectedBlocks);
}

@Test
public void shouldCompleteWhenBlocksAreEmptyExceptForWithdrawals() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this testing that when blocks are empty but have withdrawals they are still downloaded? i.e. that they are considered empty blocks. Was a bit confused from the test name

@siladu siladu Mar 7, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this testing that when blocks are empty but have withdrawals they are still downloaded?

yes, i.e. that they are not considered empty blocks like they were being before.

Any name suggestion? This was an modification of the existing test's name: shouldCompleteWithoutPeersWhenAllBlocksAreEmpty. Agree the new one could be better

final ProtocolSchedule mockProtocolSchedule = Mockito.mock(ProtocolSchedule.class);
final ProtocolSpec mockParisSpec = Mockito.mock(ProtocolSpec.class);
final ProtocolSpec mockShanghaiSpec = Mockito.mock(ProtocolSpec.class);
final WithdrawalsProcessor mockWithdrawalsProcessor = Mockito.mock(WithdrawalsProcessor.class);

final Withdrawal withdrawal =
new Withdrawal(UInt64.ONE, UInt64.ONE, Address.fromHexString("0x1"), GWei.ONE);
final List<Withdrawal> withdrawals = List.of(withdrawal);
final Hash withdrawalsRoot = BodyValidation.withdrawalsRoot(withdrawals);

final BlockHeader preWithdrawalsHeader = new BlockHeaderTestFixture().number(1).buildHeader();
final BlockHeader firstWithdrawalsHeader =
new BlockHeaderTestFixture().number(2).withdrawalsRoot(withdrawalsRoot).buildHeader();
final BlockHeader secondWithdrawalsHeader =
new BlockHeaderTestFixture().number(3).withdrawalsRoot(Hash.EMPTY_TRIE_HASH).buildHeader();

final Block block1 = new Block(preWithdrawalsHeader, BlockBody.empty());
Comment thread
gfukushima marked this conversation as resolved.
Outdated
final Block block2 =
new Block(
firstWithdrawalsHeader,
new BlockBody(
Collections.emptyList(), Collections.emptyList(), Optional.of(withdrawals)));
final Block block3 =
Comment thread
gfukushima marked this conversation as resolved.
new Block(
secondWithdrawalsHeader,
new BlockBody(
Collections.emptyList(),
Collections.emptyList(),
Optional.of(Collections.emptyList())));
final List<Block> expected = asList(block1, block2, block3);

final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
final RespondingEthPeer.Responder responder = responderForFakeBlocks(expected);
Comment thread
gfukushima marked this conversation as resolved.

when(mockProtocolSchedule.getByBlockHeader((eq(preWithdrawalsHeader))))
.thenReturn(mockParisSpec);
when(mockParisSpec.getWithdrawalsProcessor()).thenReturn(Optional.empty());
when(mockProtocolSchedule.getByBlockHeader((eq(secondWithdrawalsHeader))))
.thenReturn(mockShanghaiSpec);
when(mockShanghaiSpec.getWithdrawalsProcessor())
.thenReturn(Optional.of(mockWithdrawalsProcessor));

final EthTask<List<Block>> task =
CompleteBlocksTask.forHeaders(
mockProtocolSchedule,
ethContext,
List.of(preWithdrawalsHeader, firstWithdrawalsHeader, secondWithdrawalsHeader),
maxRetries,
new NoOpMetricsSystem());

final CompletableFuture<List<Block>> runningTask = task.run();

assertThat(runningTask).isNotDone();
respondingPeer.respond(responder);
assertThat(runningTask).isCompletedWithValue(expected);
// assertThat(task.run()).isCompletedWithValue(expected);
Comment thread
gfukushima marked this conversation as resolved.
Outdated
}

private RespondingEthPeer.Responder responderForFakeBlocks(final List<Block> blocks) {
final Blockchain mockBlockchain = spy(blockchain);
for (Block block : blocks) {
when(mockBlockchain.getBlockBody(block.getHash())).thenReturn(Optional.of(block.getBody()));
}

return RespondingEthPeer.blockchainResponder(mockBlockchain);
}

@SuppressWarnings("unchecked")
@Test
public void shouldReduceTheBlockSegmentSizeAfterEachRetry() {
Expand Down