|
|
|
@ -13,6 +13,7 @@ |
|
|
|
|
package tech.pegasys.pantheon.ethereum.eth.sync.worldstate; |
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
|
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|
|
|
|
import static org.mockito.ArgumentMatchers.any; |
|
|
|
|
import static org.mockito.Mockito.mock; |
|
|
|
|
import static org.mockito.Mockito.never; |
|
|
|
@ -30,12 +31,14 @@ import tech.pegasys.pantheon.ethereum.core.Hash; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.core.MutableWorldState; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.core.WorldState; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManager; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManagerTestUtil; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer.Responder; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.messages.EthPV63; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.messages.GetNodeDataMessage; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; |
|
|
|
|
import tech.pegasys.pantheon.ethereum.rlp.RLP; |
|
|
|
@ -127,14 +130,7 @@ public class WorldStateDownloaderTest { |
|
|
|
|
WorldStateStorage localStorage = |
|
|
|
|
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); |
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
new WorldStateDownloader( |
|
|
|
|
ethProtocolManager.ethContext(), |
|
|
|
|
localStorage, |
|
|
|
|
queue, |
|
|
|
|
10, |
|
|
|
|
10, |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
createDownloader(ethProtocolManager.ethContext(), localStorage, queue); |
|
|
|
|
|
|
|
|
|
CompletableFuture<Void> future = downloader.run(header); |
|
|
|
|
assertThat(future).isDone(); |
|
|
|
@ -172,14 +168,7 @@ public class WorldStateDownloaderTest { |
|
|
|
|
|
|
|
|
|
TaskQueue<NodeDataRequest> queue = new InMemoryTaskQueue<>(); |
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
new WorldStateDownloader( |
|
|
|
|
ethProtocolManager.ethContext(), |
|
|
|
|
storage, |
|
|
|
|
queue, |
|
|
|
|
10, |
|
|
|
|
10, |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
createDownloader(ethProtocolManager.ethContext(), storage, queue); |
|
|
|
|
|
|
|
|
|
CompletableFuture<Void> future = downloader.run(header); |
|
|
|
|
assertThat(future).isDone(); |
|
|
|
@ -220,14 +209,7 @@ public class WorldStateDownloaderTest { |
|
|
|
|
WorldStateStorage localStorage = |
|
|
|
|
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); |
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
new WorldStateDownloader( |
|
|
|
|
ethProtocolManager.ethContext(), |
|
|
|
|
localStorage, |
|
|
|
|
queue, |
|
|
|
|
10, |
|
|
|
|
10, |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
createDownloader(ethProtocolManager.ethContext(), localStorage, queue); |
|
|
|
|
|
|
|
|
|
CompletableFuture<Void> result = downloader.run(header); |
|
|
|
|
|
|
|
|
@ -289,14 +271,7 @@ public class WorldStateDownloaderTest { |
|
|
|
|
localStorageUpdater.commit(); |
|
|
|
|
|
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
new WorldStateDownloader( |
|
|
|
|
ethProtocolManager.ethContext(), |
|
|
|
|
localStorage, |
|
|
|
|
queue, |
|
|
|
|
10, |
|
|
|
|
10, |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
createDownloader(ethProtocolManager.ethContext(), localStorage, queue); |
|
|
|
|
|
|
|
|
|
CompletableFuture<Void> result = downloader.run(header); |
|
|
|
|
|
|
|
|
@ -369,14 +344,7 @@ public class WorldStateDownloaderTest { |
|
|
|
|
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); |
|
|
|
|
|
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
new WorldStateDownloader( |
|
|
|
|
ethProtocolManager.ethContext(), |
|
|
|
|
localStorage, |
|
|
|
|
queue, |
|
|
|
|
10, |
|
|
|
|
10, |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
createDownloader(ethProtocolManager.ethContext(), localStorage, queue); |
|
|
|
|
|
|
|
|
|
CompletableFuture<Void> result = downloader.run(header); |
|
|
|
|
|
|
|
|
@ -464,14 +432,7 @@ public class WorldStateDownloaderTest { |
|
|
|
|
localStorageUpdater.commit(); |
|
|
|
|
|
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
new WorldStateDownloader( |
|
|
|
|
ethProtocolManager.ethContext(), |
|
|
|
|
localStorage, |
|
|
|
|
queue, |
|
|
|
|
10, |
|
|
|
|
10, |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
createDownloader(ethProtocolManager.ethContext(), localStorage, queue); |
|
|
|
|
|
|
|
|
|
CompletableFuture<Void> result = downloader.run(header); |
|
|
|
|
|
|
|
|
@ -570,14 +531,7 @@ public class WorldStateDownloaderTest { |
|
|
|
|
localStorageUpdater.commit(); |
|
|
|
|
|
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
new WorldStateDownloader( |
|
|
|
|
ethProtocolManager.ethContext(), |
|
|
|
|
localStorage, |
|
|
|
|
queue, |
|
|
|
|
10, |
|
|
|
|
10, |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
createDownloader(ethProtocolManager.ethContext(), localStorage, queue); |
|
|
|
|
|
|
|
|
|
CompletableFuture<Void> result = downloader.run(header); |
|
|
|
|
|
|
|
|
@ -616,6 +570,70 @@ public class WorldStateDownloaderTest { |
|
|
|
|
assertAccountsMatch(localWorldState, accounts); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void stalledDownloader() { |
|
|
|
|
simulateStalledDownload(10); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void stalledDownloaderWithOneRetry() { |
|
|
|
|
simulateStalledDownload(1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
public void stalledDownloaderWithNoRetries() { |
|
|
|
|
simulateStalledDownload(0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void simulateStalledDownload(final int maxRetries) { |
|
|
|
|
final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); |
|
|
|
|
BlockDataGenerator dataGen = new BlockDataGenerator(1); |
|
|
|
|
|
|
|
|
|
// Setup "remote" state
|
|
|
|
|
final WorldStateStorage remoteStorage = |
|
|
|
|
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); |
|
|
|
|
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage); |
|
|
|
|
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable(); |
|
|
|
|
|
|
|
|
|
// Generate accounts and save corresponding state root
|
|
|
|
|
dataGen.createRandomAccounts(remoteWorldState, 10); |
|
|
|
|
final Hash stateRoot = remoteWorldState.rootHash(); |
|
|
|
|
assertThat(stateRoot).isNotEqualTo(EMPTY_TRIE_ROOT); // Sanity check
|
|
|
|
|
final BlockHeader header = |
|
|
|
|
dataGen.block(BlockOptions.create().setStateRoot(stateRoot).setBlockNumber(10)).getHeader(); |
|
|
|
|
|
|
|
|
|
TaskQueue<NodeDataRequest> queue = new InMemoryTaskQueue<>(); |
|
|
|
|
WorldStateStorage localStorage = |
|
|
|
|
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); |
|
|
|
|
SynchronizerConfiguration syncConfig = |
|
|
|
|
SynchronizerConfiguration.builder().worldStateRequestMaxRetries(maxRetries).build(); |
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
createDownloader(syncConfig, ethProtocolManager.ethContext(), localStorage, queue); |
|
|
|
|
|
|
|
|
|
// Create a peer that can respond
|
|
|
|
|
RespondingEthPeer peer = |
|
|
|
|
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber()); |
|
|
|
|
|
|
|
|
|
// Start downloader
|
|
|
|
|
CompletableFuture<?> result = downloader.run(header); |
|
|
|
|
// A second run should return an error without impacting the first result
|
|
|
|
|
CompletableFuture<?> secondResult = downloader.run(header); |
|
|
|
|
assertThat(secondResult).isCompletedExceptionally(); |
|
|
|
|
assertThat(result).isNotCompletedExceptionally(); |
|
|
|
|
|
|
|
|
|
Responder emptyResponder = RespondingEthPeer.emptyResponder(); |
|
|
|
|
for (int i = 0; i < maxRetries; i++) { |
|
|
|
|
peer.respond(emptyResponder); |
|
|
|
|
} |
|
|
|
|
// Downloader should not be done yet
|
|
|
|
|
assertThat(result).isNotDone(); |
|
|
|
|
|
|
|
|
|
// One more empty response should trigger a failure
|
|
|
|
|
peer.respond(emptyResponder); |
|
|
|
|
assertThat(result).isCompletedExceptionally(); |
|
|
|
|
assertThatThrownBy(result::get).hasCauseInstanceOf(StalledDownloadException.class); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Walks through trie represented by the given rootHash and returns hash-node pairs that would |
|
|
|
|
* need to be requested from the network in order to reconstruct this trie. |
|
|
|
@ -700,15 +718,13 @@ public class WorldStateDownloaderTest { |
|
|
|
|
WorldStateStorage localStorage = |
|
|
|
|
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); |
|
|
|
|
WorldStateArchive localWorldStateArchive = new WorldStateArchive(localStorage); |
|
|
|
|
SynchronizerConfiguration syncConfig = |
|
|
|
|
SynchronizerConfiguration.builder() |
|
|
|
|
.worldStateHashCountPerRequest(hashesPerRequest) |
|
|
|
|
.worldStateRequestParallelism(maxOutstandingRequests) |
|
|
|
|
.build(); |
|
|
|
|
WorldStateDownloader downloader = |
|
|
|
|
new WorldStateDownloader( |
|
|
|
|
ethProtocolManager.ethContext(), |
|
|
|
|
localStorage, |
|
|
|
|
queue, |
|
|
|
|
hashesPerRequest, |
|
|
|
|
maxOutstandingRequests, |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
createDownloader(syncConfig, ethProtocolManager.ethContext(), localStorage, queue); |
|
|
|
|
|
|
|
|
|
// Create some peers that can respond
|
|
|
|
|
List<RespondingEthPeer> usefulPeers = |
|
|
|
@ -828,6 +844,29 @@ public class WorldStateDownloaderTest { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private WorldStateDownloader createDownloader( |
|
|
|
|
final EthContext context, |
|
|
|
|
final WorldStateStorage storage, |
|
|
|
|
final TaskQueue<NodeDataRequest> queue) { |
|
|
|
|
return createDownloader(SynchronizerConfiguration.builder().build(), context, storage, queue); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private WorldStateDownloader createDownloader( |
|
|
|
|
final SynchronizerConfiguration config, |
|
|
|
|
final EthContext context, |
|
|
|
|
final WorldStateStorage storage, |
|
|
|
|
final TaskQueue<NodeDataRequest> queue) { |
|
|
|
|
return new WorldStateDownloader( |
|
|
|
|
context, |
|
|
|
|
storage, |
|
|
|
|
queue, |
|
|
|
|
config.getWorldStateHashCountPerRequest(), |
|
|
|
|
config.getWorldStateRequestParallelism(), |
|
|
|
|
config.getWorldStateRequestMaxRetries(), |
|
|
|
|
NoOpMetricsSystem.NO_OP_LABELLED_TIMER, |
|
|
|
|
new NoOpMetricsSystem()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@FunctionalInterface |
|
|
|
|
private interface NetworkResponder { |
|
|
|
|
void respond( |
|
|
|
|