From c29db3fc6ceff69bb0f2b9bc7926537f7f62832d Mon Sep 17 00:00:00 2001 From: Justin Florentine Date: Fri, 10 Nov 2023 16:45:55 -0500 Subject: [PATCH] Release 23.10.x (#6155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Unsigned timestamps and blob gas used (#6046) * lots of places an unsigned timestamp is a problem * adds unchecked annotations to OptionalUnsignedLong rpc parameter type --------- Signed-off-by: Justin Florentine (cherry picked from commit a90ea056f19915a4eca787fc066c080e1789c14c) * [MINOR] ux improvements (#6049) * log command line option that is affected * made plugins summary log part of config overview * check for null plugin context Signed-off-by: Sally MacFarlane --------- Signed-off-by: Sally MacFarlane (cherry picked from commit e8bca6101c5d84c4501dda13b92420e44138a91b) * Update changelog release (#6062) Signed-off-by: Gabriel-Trintinalia (cherry picked from commit f81088789eeea6fc2479393087bd5791f2e70ece) * Dencun corner cases (#6060) * cherry pick changes from https://github.com/hyperledger/besu/pull/6054/files\#diff-22b78733e37a697fa8d1d8a02d2a87fe5ccea9cf67c34ce5e6311f024c14abd6L643-R738 Signed-off-by: Sally MacFarlane * cherry pick changes from https://github.com/hyperledger/besu/pull/6054/files\#diff-61db834b59eae5ce5c438462505de1add8fa244deda830742060d15f668a9806R39-R44 Signed-off-by: Sally MacFarlane * formatting Signed-off-by: Sally MacFarlane * update the EIP-6110 acceptance tests Signed-off-by: Danno Ferrin --------- Signed-off-by: Sally MacFarlane Signed-off-by: Danno Ferrin Co-authored-by: Danno Ferrin (cherry picked from commit 9d9fe8c201daf4a386457794a57d0776e6a91437) * add retry logic on sync pipeline for rocksdb issue (#6004) * add retry logic for sync pipeline with rocksdb issue Signed-off-by: Karim TAAM (cherry picked from commit c839a3b02298c6b00ba0867cc0155d760fe7f43a) * Mining options refactor (#6027) Signed-off-by: Fabio Di Fabio Co-authored-by: Sally MacFarlane (cherry picked from commit 20a82d45482194129e68133973533c290be8eb1c) * Update reference tests to Cancun (#6054) * Update reference tests to Cancun Update reference tests to cancun tests. Signed-off-by: Danno Ferrin * update the subrepo Signed-off-by: Danno Ferrin * update the EIP-6110 acceptance tests Signed-off-by: Danno Ferrin * update to develop Signed-off-by: Danno Ferrin --------- Signed-off-by: Danno Ferrin Signed-off-by: Sally MacFarlane Co-authored-by: Sally MacFarlane (cherry picked from commit 909649f26cd5ff2722cafcb876baf83c886ae474) * Optimize GetPooledTransactionsFromPeerTask with HashSet (#6071) Switch from using a List to a HashSet for transaction hashes in GetPooledTransactionsFromPeerTask to improve performance. Signed-off-by: Suyash Nayan Signed-off-by: Sally MacFarlane Co-authored-by: Sally MacFarlane (cherry picked from commit a2dbb8272c47f537261819da565a948902e1a514) * Decouple TrieLogManager and CachedWorldStorageManager (#6072) Separate out the concepts of world state caching and trie log management. Remove AbstractTrieLogManager and make TrieLogManager the concrete implementation. Signed-off-by: Simon Dudley (cherry picked from commit 20f518ed43612cc6133b727e7706ddcea3bacec9) * Implement miner_setMinPriorityFee and miner_getMinPriorityFee (#6080) * Refactor mining options Signed-off-by: Fabio Di Fabio * Fix null pointer exception Signed-off-by: Fabio Di Fabio * fix another null pointer exception Signed-off-by: Fabio Di Fabio * uncomment code Signed-off-by: Fabio Di Fabio * Move miner options tests Signed-off-by: Fabio Di Fabio * Unit test fixes Signed-off-by: Fabio Di Fabio * Removed the commented code Signed-off-by: Fabio Di Fabio * WIP Signed-off-by: Fabio Di Fabio * WIP Signed-off-by: Fabio Di Fabio * New miner option: min-priority-fee Signed-off-by: Fabio Di Fabio * Remove not relevant for this feature Signed-off-by: Fabio Di Fabio * Remove not relevant for this feature Signed-off-by: Fabio Di Fabio * Fix javadoc Signed-off-by: Fabio Di Fabio * Remove code not belonging to this PR Signed-off-by: Fabio Di Fabio * coinbase is an updatable parameter Signed-off-by: Fabio Di Fabio * Move MiningOptions to upper package Signed-off-by: Fabio Di Fabio * Fix coinbase for *bft Signed-off-by: Fabio Di Fabio * Implement methods to get and set min priority fee Signed-off-by: Gabriel-Trintinalia * Fix spotless Signed-off-by: Gabriel-Trintinalia * Apply suggestions from code review Signed-off-by: Fabio Di Fabio * Update besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java [skip-ci] Co-authored-by: Sally MacFarlane Signed-off-by: Fabio Di Fabio * Add new config option to everything config Signed-off-by: Gabriel-Trintinalia * Fix unit test Signed-off-by: Gabriel-Trintinalia * Accept PR suggestions Signed-off-by: Gabriel-Trintinalia --------- Signed-off-by: Fabio Di Fabio Signed-off-by: Gabriel-Trintinalia Signed-off-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> Co-authored-by: Fabio Di Fabio Co-authored-by: Sally MacFarlane Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> (cherry picked from commit 3310c416bd0892512b42c82e37cc458efe97f662) * Use Bytes Trie to track warm addresses (#6069) * Use Bytes Trie to track warm addresses Move from a java HashSet to a custom Trie based on bytes to store the warm addresses, creates, and self-destructs. This avoids needing to calculate java hashes or engage in using custom Comparators. Signed-off-by: Danno Ferrin * codeql scan Signed-off-by: Danno Ferrin --------- Signed-off-by: Danno Ferrin Signed-off-by: Sally MacFarlane Co-authored-by: Sally MacFarlane (cherry picked from commit af011d2a0faa1876c43728b21e695e501b6f0af7) * Migrate Operand Stack to Growing Stack (#6068) To improve the performance of CALL operations move the OperandStack and ReturnStack to growing stacks instead of a fully allocated stack. Signed-off-by: Danno Ferrin (cherry picked from commit 949c724ac3d97822740a8cdcfffdb7432d2165d6) * Remove a BonsaiWorldStateProvider constructor (#6090) Was only used for supporting test code and can instead reuse static factory from InMemoryKeyValueStorageProvider Signed-off-by: Simon Dudley (cherry picked from commit 9a22703b5d4c2301e8614737c803360059c24394) * add RpcEndpointService to thread runner (#6091) Signed-off-by: Sally MacFarlane (cherry picked from commit 7ac8af043882486797209931d52637c79b01c20f) * ETC 'Spiral' network upgrade (#6078) * Add 23.10.2 section to changelog Signed-off-by: Diego López León * Set ENR tree for DNS discovery for Mordor network Signed-off-by: Diego López León * Add ECIP-1109: 'Spiral' network upgrade support Signed-off-by: Diego López León --------- Signed-off-by: Diego López León Signed-off-by: Sally MacFarlane Co-authored-by: Sally MacFarlane (cherry picked from commit f58f6cffca1c877b566ee5f6e980a53b352bd3dc) * Trigger contextEnter/Exit for all frames, including root (#6074) * Trigger contextEnter/Exit for all frames, including root * Differentiate between context entry and re-entry in `OperationTracer` * Update evm/src/test/java/org/hyperledger/besu/evm/processor/AbstractMessageProcessorTest.java Co-authored-by: Sally MacFarlane Signed-off-by: delehef --------- Signed-off-by: Franklin Delehelle Signed-off-by: delehef Signed-off-by: Sally MacFarlane Co-authored-by: Sally MacFarlane (cherry picked from commit 0345b2473f349e229af05d663b340da24741b121) * feat: add a way to read memory without altering the word capacity (#6073) * feat: add a way to read memory without altering the word capacity * add tests Signed-off-by: Daniel Lehrner Signed-off-by: Franklin Delehelle * Fix read-past-end * Do not abuse method overload * Update CHANGELOG.md Signed-off-by: delehef Signed-off-by: Franklin Delehelle * add tests for MessageFrame.shadowReadMemory Signed-off-by: Daniel Lehrner Signed-off-by: Franklin Delehelle * Straddled reads tests Signed-off-by: Franklin Delehelle --------- Signed-off-by: Franklin Delehelle Signed-off-by: Daniel Lehrner Signed-off-by: delehef Signed-off-by: delehef Co-authored-by: Daniel Lehrner (cherry picked from commit edf23cb5708f7611e0fd81b3d49ca17863a74daf) * renamed env field (#6096) Signed-off-by: Justin Florentine (cherry picked from commit 67ef9e05f9ed1da245251eb129af15a55aacea7e) * [MINOR] Upgrade netty and grpc (#6100) * Upgrade netty and grpc * fix verification file Signed-off-by: Simon Dudley --------- Signed-off-by: Sally MacFarlane Co-authored-by: Sally MacFarlane (cherry picked from commit accac1ccbcf109fe898c2ea7b04a4f6a17afc291) * TraceService: return results for transactions in block (#6087) * TraceService: return results for transactions in block Signed-off-by: Daniel Lehrner (cherry picked from commit a60b31b3af397d1b58783aa1c69da4f0c6d80bd1) * RPC Parameters to accept input and data field (#6094) * Accept input or data as payload for RPC calls Signed-off-by: Gabriel Fukushima * Add json new rpc valid and invalid request to test the changes Signed-off-by: Gabriel Fukushima * Change JsonCallParameter signature to avoid duplicating constructor Signed-off-by: Gabriel Fukushima * Add changelog Signed-off-by: Gabriel Fukushima --------- Signed-off-by: Gabriel Fukushima (cherry picked from commit 6dd558b532fd9f9c5d5ea09ba0f2c7949288c7f1) * Fix some typos (#6093) * Fix some typos * Update plugin version Signed-off-by: GoodDaisy <90915921+GoodDaisy@users.noreply.github.com> --------- Signed-off-by: GoodDaisy <90915921+GoodDaisy@users.noreply.github.com> (cherry picked from commit e814886d4aa9f64b0f24360e7246e22f3bff574b) * Correct reference test blobgas calculation (#6107) * Correct reference test blobgas calculation Fix tpyo that resulted in an NPE in t8n blob gas calculations. Signed-off-by: Danno Ferrin * changelog Signed-off-by: Danno Ferrin * spotless Signed-off-by: Danno Ferrin --------- Signed-off-by: Danno Ferrin (cherry picked from commit d0a6a70cc8c1cad977b08f6ad1e5a72796b08f13) * Restore javadoc and sources jar as trusted artifacts (#6109) Makes Idea happy again as documented here https://docs.gradle.org/6.8.3/userguide/dependency_verification.html#sec:skipping-javadocs Signed-off-by: Fabio Di Fabio (cherry picked from commit 311570f4900442fa44e2ba36617a7b9846a2ece8) * Add API to set and get the minGasPrice at runtime (#6097) Signed-off-by: Fabio Di Fabio (cherry picked from commit de8ca108a36680f8db9e9bf59f1b5b1a6d141a0f) * Don't put NONCE_TOO_LOW transactions into the invalid nonce cache (#6067) * Don't put NONCE_TOO_LOW transactions into the invalid nonce cache Signed-off-by: Matthew Whitehead * Update unit tests Signed-off-by: Matthew Whitehead * Use list of errors to ignore Signed-off-by: Matthew Whitehead --------- Signed-off-by: Matthew Whitehead Signed-off-by: Matt Whitehead Signed-off-by: Fabio Di Fabio Co-authored-by: Fabio Di Fabio (cherry picked from commit 84dee295d9c31b371bf6d68da169129ed5bb5c01) * Journaled world state (#6023) Introduce a new Journaled World State Updater. Within a transaction it keeps one copy of account and storage state, restoring previous revisions on reverts and exceptional halts. This updater only supports post-merge semantics with regard to empty accounts, namely that they do not exist in world state. Adds an EvmConfiguration option for stacked vs journaled updater, and wire it in where needed. The staked updater is default mode, which is the current behavior prior to this patch. Signed-off-by: Danno Ferrin (cherry picked from commit 094c8416df9cf003e0a7d1fb0651d34468137896) * setMinPriorityFee - Return Invalid Param when invalid and use hexadecimal instead of Long (#6099) Signed-off-by: Gabriel-Trintinalia (cherry picked from commit 7acdd879a1b28a67d913f6cf7834273175c165b4) * [MINOR] - Add 23.10.1 correct changelog (#6110) Signed-off-by: Gabriel-Trintinalia (cherry picked from commit 236779d3ceb216aa4155258f228032a75c05d6d2) * Add transaction selector based on min priority fee parameter (#6083) Signed-off-by: Gabriel-Trintinalia (cherry picked from commit 41b95758b4d27b61a933b2dbbdc3c9eaaffbb2d6) * Add config option to clique to allow not creating empty blocks (#6082) Signed-off-by: Jason Frame (cherry picked from commit 6a2d41fccc00eac110a8cec27bc16cc2df47da0a) * load all accounts into cache (#6101) There was a slight problem on the bonsai side because all account reads did not go through a single method. One of the two add the account to the cache but the other did not. This had two consequences: Less good performance because certain accounts had to be read several times and also all account reads were not marked in the trielog. This will fix both problems. Signed-off-by: Karim TAAM (cherry picked from commit d049cb3e34b92d160b181e36329a84671703e068) * Reference Tests v13 (#6114) v13 of the official Ethereum Reference Tests. Signed-off-by: Danno Ferrin (cherry picked from commit cea3d8a71aa78fd775869e44bbd772f3d1e36779) * clean up the ProcessableBlockHeader (#6117) * clean up Signed-off-by: stefan.pingel@consensys.net (cherry picked from commit 646c5a3bc63427cbe098139b9a4fa73fac50153e) * Log missing chain head as warning, not trace (#6127) Signed-off-by: Matthew Whitehead (cherry picked from commit 83ae69a6fa349e2418c91336a9df3f33845fce39) * Update RPCs for yParity (#6119) Update the GraphQL and JSON-RPC endpoints to provide `yParity` instead of `v` for non-legacy transactions. Update the JSON-RPC tests to use the Hive data. Add tests for Shanghai and Cancun Blocks. Signed-off-by: Danno Ferrin (cherry picked from commit 228424215cb6edcb5cd9fa33835ee588d414d7ab) * Make tracer in block building block aware (#6135) Signed-off-by: stefan.pingel@consensys.net (cherry picked from commit f42f8c1122bda2618277e72dc5799d2700ac0caf) * Revert "Discard invalid transaction" warning logs (#6137) * Revert "Log missing chain head as warning, not trace (#6127)" * leave chain head at warning, just revert the discard warning Signed-off-by: garyschulte (cherry picked from commit 914ab792eadd091da48715576d461a66b0aaaecf) * Reverse sort order (#6106) * Reverse added order and sequence number Signed-off-by: Matthew Whitehead * Remove extraneous 'addedAt' check Signed-off-by: Matthew Whitehead --------- Signed-off-by: Matthew Whitehead (cherry picked from commit 0203092d19146405a6ddd7f18be63c10030627b3) * Force tx replacement price bump to zero when zero base fee market is configured (#6079) Signed-off-by: Fabio Di Fabio (cherry picked from commit 636ad8a65a061d8a66686b5fb86de86958197308) * Time limited block creation (#6044) Signed-off-by: Fabio Di Fabio Co-authored-by: Sally MacFarlane (cherry picked from commit 8319fae725486547ad4198f6e8751cdc880099f8) * Limit memory used in handling invalid blocks (#6138) Signed-off-by: Jason Frame (cherry picked from commit 701cbb000f7e0205e2e7f9f5bbe103e3343d2937) * Delete leftPad when capturing the stack before and after a frame execution (#6102) * Delete leftPad when capturing the stack before and after the execution * Still use leftPad when displaying the stack in the output (ex. for debug_traceTransaction) * Fix integration test * Use StringBuilder to left pad the hex representation of a 32 bytes Signed-off-by: Ameziane H (cherry picked from commit 9710a9adaac1f131c1e2e05f5aa53eba4fdc13bd) * fix yParity flakey test (#6151) Fix the flakeiness in EthGetTransactionByHashTest as well as some other sonar identified cleanup. Signed-off-by: Danno Ferrin (cherry picked from commit 4b58d071f6c34ce4e79c0f2ce7ce8d43a520e1ee) * Apply the same reverse sort order as https://github.com/hyperledger/b… (#6146) * Apply the same reverse sort order as https://github.com/hyperledger/besu/pull/6106 but to the base fee sorter Signed-off-by: Matthew Whitehead * Fix unit tests Signed-off-by: Matthew Whitehead * Update eviction unit tests to expect highest-sequence TXs be evicted first Signed-off-by: Matthew Whitehead * Update change log Signed-off-by: Matthew Whitehead * Spotless fixes Signed-off-by: Matthew Whitehead --------- Signed-off-by: Matthew Whitehead (cherry picked from commit 8afad4159403776f43b30fa5071c7d3e46689365) --------- Co-authored-by: Sally MacFarlane Co-authored-by: Gabriel-Trintinalia Co-authored-by: matkt Co-authored-by: Fabio Di Fabio Co-authored-by: Danno Ferrin Co-authored-by: Suyash Nayan <89125422+7suyash7@users.noreply.github.com> Co-authored-by: Simon Dudley Co-authored-by: Diego López León Co-authored-by: delehef Co-authored-by: daniellehrner Co-authored-by: Gabriel Fukushima Co-authored-by: GoodDaisy <90915921+GoodDaisy@users.noreply.github.com> Co-authored-by: Matt Whitehead Co-authored-by: Jason Frame Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> Co-authored-by: garyschulte Co-authored-by: ahamlat --- ethereum/eth/build.gradle | 1 + .../eth/sync/StorageExceptionManager.java | 64 +++++++++++++++ .../fastsync/worldstate/PersistDataStep.java | 63 ++++++++++----- .../eth/sync/snapsync/LoadLocalDataStep.java | 50 +++++++++--- .../eth/sync/snapsync/PersistDataStep.java | 80 ++++++++++++------- .../SnapWorldStateDownloadProcess.java | 16 ++++ .../ethereum/eth/sync/snapsync/StackTrie.java | 15 +++- .../request/AccountRangeDataRequest.java | 6 ++ .../snapsync/request/BytecodeRequest.java | 5 ++ .../request/StorageRangeDataRequest.java | 6 ++ .../request/heal/TrieNodeHealingRequest.java | 5 ++ gradle.properties | 2 +- 12 files changed, 252 insertions(+), 61 deletions(-) create mode 100644 ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/StorageExceptionManager.java diff --git a/ethereum/eth/build.gradle b/ethereum/eth/build.gradle index a40b49afffb..1919efdd4f2 100644 --- a/ethereum/eth/build.gradle +++ b/ethereum/eth/build.gradle @@ -58,6 +58,7 @@ dependencies { implementation 'io.tmio:tuweni-bytes' implementation 'io.tmio:tuweni-units' implementation 'io.tmio:tuweni-rlp' + implementation 'org.rocksdb:rocksdbjni' annotationProcessor "org.immutables:value" implementation "org.immutables:value-annotations" diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/StorageExceptionManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/StorageExceptionManager.java new file mode 100644 index 00000000000..97d1506cf3e --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/StorageExceptionManager.java @@ -0,0 +1,64 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.eth.sync; + +import org.hyperledger.besu.plugin.services.exception.StorageException; + +import java.util.EnumSet; +import java.util.Optional; + +import org.rocksdb.RocksDBException; +import org.rocksdb.Status; + +public final class StorageExceptionManager { + + private static final EnumSet RETRYABLE_STATUS_CODES = + EnumSet.of(Status.Code.TimedOut, Status.Code.TryAgain, Status.Code.Busy); + + private static final long ERROR_THRESHOLD = 1000; + + private static long retryableErrorCounter; + /** + * Determines if an operation can be retried based on the error received. This method checks if + * the cause of the StorageException is a RocksDBException. If it is, it retrieves the status code + * of the RocksDBException and checks if it is contained in the list of retryable {@link + * StorageExceptionManager.RETRYABLE_STATUS_CODES} status codes. + * + * @param e the StorageException to check + * @return true if the operation can be retried, false otherwise + */ + public static boolean canRetryOnError(final StorageException e) { + return Optional.of(e.getCause()) + .filter(z -> z instanceof RocksDBException) + .map(RocksDBException.class::cast) + .map(RocksDBException::getStatus) + .map(Status::getCode) + .map(RETRYABLE_STATUS_CODES::contains) + .map( + result -> { + retryableErrorCounter++; + return result; + }) + .orElse(false); + } + + public static long getRetryableErrorCounter() { + return retryableErrorCounter; + } + + public static boolean errorCountAtThreshold() { + return retryableErrorCounter % ERROR_THRESHOLD == 1; + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/PersistDataStep.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/PersistDataStep.java index 94592b98d81..1ab202ee6ce 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/PersistDataStep.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/PersistDataStep.java @@ -14,15 +14,26 @@ */ package org.hyperledger.besu.ethereum.eth.sync.fastsync.worldstate; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.canRetryOnError; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.errorCountAtThreshold; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.getRetryableErrorCounter; + import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.sync.worldstate.WorldDownloadState; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage.Updater; +import org.hyperledger.besu.plugin.services.exception.StorageException; import org.hyperledger.besu.services.tasks.Task; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class PersistDataStep { + + private static final Logger LOG = LoggerFactory.getLogger(PersistDataStep.class); + private final WorldStateStorage worldStateStorage; public PersistDataStep(final WorldStateStorage worldStateStorage) { @@ -33,24 +44,40 @@ public List> persist( final List> tasks, final BlockHeader blockHeader, final WorldDownloadState downloadState) { - final Updater updater = worldStateStorage.updater(); - tasks.stream() - .map( - task -> { - enqueueChildren(task, downloadState); - return task; - }) - .map(Task::getData) - .filter(request -> request.getData() != null) - .forEach( - request -> { - if (isRootState(blockHeader, request)) { - downloadState.setRootNodeData(request.getData()); - } else { - request.persist(updater); - } - }); - updater.commit(); + try { + final Updater updater = worldStateStorage.updater(); + tasks.stream() + .map( + task -> { + enqueueChildren(task, downloadState); + return task; + }) + .map(Task::getData) + .filter(request -> request.getData() != null) + .forEach( + request -> { + if (isRootState(blockHeader, request)) { + downloadState.setRootNodeData(request.getData()); + } else { + request.persist(updater); + } + }); + updater.commit(); + } catch (StorageException storageException) { + if (canRetryOnError(storageException)) { + // We reset the task by setting it to null. This way, it is considered as failed by the + // pipeline, and it will attempt to execute it again later. + if (errorCountAtThreshold()) { + LOG.info( + "Encountered {} retryable RocksDB errors, latest error message {}", + getRetryableErrorCounter(), + storageException.getMessage()); + } + tasks.forEach(nodeDataRequestTask -> nodeDataRequestTask.getData().setData(null)); + } else { + throw storageException; + } + } return tasks; } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/LoadLocalDataStep.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/LoadLocalDataStep.java index 5264eac2b50..c24dbf6037d 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/LoadLocalDataStep.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/LoadLocalDataStep.java @@ -14,11 +14,16 @@ */ package org.hyperledger.besu.ethereum.eth.sync.snapsync; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.canRetryOnError; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.errorCountAtThreshold; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.getRetryableErrorCounter; + import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.SnapDataRequest; import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.heal.TrieNodeHealingRequest; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage; import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.exception.StorageException; import org.hyperledger.besu.plugin.services.metrics.Counter; import org.hyperledger.besu.services.pipeline.Pipe; import org.hyperledger.besu.services.tasks.Task; @@ -27,9 +32,12 @@ import java.util.stream.Stream; import org.apache.tuweni.bytes.Bytes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LoadLocalDataStep { + private static final Logger LOG = LoggerFactory.getLogger(LoadLocalDataStep.class); private final WorldStateStorage worldStateStorage; private final SnapWorldDownloadState downloadState; private final SnapSyncProcessState snapSyncState; @@ -58,19 +66,35 @@ public Stream> loadLocalDataTrieNode( final Task task, final Pipe> completedTasks) { final TrieNodeHealingRequest request = (TrieNodeHealingRequest) task.getData(); // check if node is already stored in the worldstate - if (snapSyncState.hasPivotBlockHeader()) { - Optional existingData = request.getExistingData(downloadState, worldStateStorage); - if (existingData.isPresent()) { - existingNodeCounter.inc(); - request.setData(existingData.get()); - request.setRequiresPersisting(false); - final WorldStateStorage.Updater updater = worldStateStorage.updater(); - request.persist( - worldStateStorage, updater, downloadState, snapSyncState, snapSyncConfiguration); - updater.commit(); - downloadState.enqueueRequests(request.getRootStorageRequests(worldStateStorage)); - completedTasks.put(task); - return Stream.empty(); + try { + if (snapSyncState.hasPivotBlockHeader()) { + Optional existingData = request.getExistingData(downloadState, worldStateStorage); + if (existingData.isPresent()) { + existingNodeCounter.inc(); + request.setData(existingData.get()); + request.setRequiresPersisting(false); + final WorldStateStorage.Updater updater = worldStateStorage.updater(); + request.persist( + worldStateStorage, updater, downloadState, snapSyncState, snapSyncConfiguration); + updater.commit(); + downloadState.enqueueRequests(request.getRootStorageRequests(worldStateStorage)); + completedTasks.put(task); + return Stream.empty(); + } + } + } catch (StorageException storageException) { + if (canRetryOnError(storageException)) { + // We reset the task by setting it to null. This way, it is considered as failed by the + // pipeline, and it will attempt to execute it again later. + if (errorCountAtThreshold()) { + LOG.info( + "Encountered {} retryable RocksDB errors, latest error message {}", + getRetryableErrorCounter(), + storageException.getMessage()); + } + task.getData().clear(); + } else { + throw storageException; } } return Stream.of(task); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/PersistDataStep.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/PersistDataStep.java index 6a39f648716..df3696ccdf7 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/PersistDataStep.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/PersistDataStep.java @@ -14,16 +14,25 @@ */ package org.hyperledger.besu.ethereum.eth.sync.snapsync; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.canRetryOnError; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.errorCountAtThreshold; +import static org.hyperledger.besu.ethereum.eth.sync.StorageExceptionManager.getRetryableErrorCounter; + import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage; import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.SnapDataRequest; import org.hyperledger.besu.ethereum.eth.sync.snapsync.request.heal.TrieNodeHealingRequest; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage; +import org.hyperledger.besu.plugin.services.exception.StorageException; import org.hyperledger.besu.services.tasks.Task; import java.util.List; import java.util.stream.Stream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class PersistDataStep { + private static final Logger LOG = LoggerFactory.getLogger(PersistDataStep.class); private final SnapSyncProcessState snapSyncState; private final WorldStateStorage worldStateStorage; @@ -43,41 +52,58 @@ public PersistDataStep( } public List> persist(final List> tasks) { - final WorldStateStorage.Updater updater = worldStateStorage.updater(); - for (Task task : tasks) { - if (task.getData().isResponseReceived()) { - // enqueue child requests - final Stream childRequests = - task.getData().getChildRequests(downloadState, worldStateStorage, snapSyncState); - if (!(task.getData() instanceof TrieNodeHealingRequest)) { - enqueueChildren(childRequests); - } else { - if (!task.getData().isExpired(snapSyncState)) { + try { + final WorldStateStorage.Updater updater = worldStateStorage.updater(); + for (Task task : tasks) { + if (task.getData().isResponseReceived()) { + // enqueue child requests + final Stream childRequests = + task.getData().getChildRequests(downloadState, worldStateStorage, snapSyncState); + if (!(task.getData() instanceof TrieNodeHealingRequest)) { enqueueChildren(childRequests); } else { - continue; + if (!task.getData().isExpired(snapSyncState)) { + enqueueChildren(childRequests); + } else { + continue; + } } - } - // persist nodes - final int persistedNodes = - task.getData() - .persist( - worldStateStorage, - updater, - downloadState, - snapSyncState, - snapSyncConfiguration); - if (persistedNodes > 0) { - if (task.getData() instanceof TrieNodeHealingRequest) { - downloadState.getMetricsManager().notifyTrieNodesHealed(persistedNodes); - } else { - downloadState.getMetricsManager().notifyNodesGenerated(persistedNodes); + // persist nodes + final int persistedNodes = + task.getData() + .persist( + worldStateStorage, + updater, + downloadState, + snapSyncState, + snapSyncConfiguration); + if (persistedNodes > 0) { + if (task.getData() instanceof TrieNodeHealingRequest) { + downloadState.getMetricsManager().notifyTrieNodesHealed(persistedNodes); + } else { + downloadState.getMetricsManager().notifyNodesGenerated(persistedNodes); + } } } } + updater.commit(); + } catch (StorageException storageException) { + if (canRetryOnError(storageException)) { + // We reset the task by setting it to null. This way, it is considered as failed by the + // pipeline, and it will attempt to execute it again later. not display all the retryable + // issues + if (errorCountAtThreshold()) { + LOG.info( + "Encountered {} retryable RocksDB errors, latest error message {}", + getRetryableErrorCounter(), + storageException.getMessage()); + } + tasks.forEach(task -> task.getData().clear()); + } else { + throw storageException; + } } - updater.commit(); return tasks; } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldStateDownloadProcess.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldStateDownloadProcess.java index c8afc582b72..c19ae6facc7 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldStateDownloadProcess.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapWorldStateDownloadProcess.java @@ -231,6 +231,22 @@ public SnapWorldStateDownloadProcess build() { "step", "action"); + /* + The logic and intercommunication of different pipelines can be summarized as follows: + + 1. Account Data Pipeline (fetchAccountDataPipeline): This process starts with downloading the leaves of the account tree in ranges, with multiple ranges being processed simultaneously. + If the downloaded accounts are smart contracts, tasks are created in the storage pipeline to download the storage tree of the smart contract, and in the code download pipeline for the smart contract. + + 2. Storage Data Pipeline (fetchStorageDataPipeline): Running parallel to the account data pipeline, this pipeline downloads the storage of smart contracts. + If all slots cannot be downloaded at once, tasks are created in the fetchLargeStorageDataPipeline to download the storage by range, allowing parallelization of large account downloads. + + 3. Code Data Pipeline (fetchCodePipeline): This pipeline, running concurrently with the account and storage data pipelines, is responsible for downloading the code of the smart contracts. + + 4. Large Storage Data Pipeline (fetchLargeStorageDataPipeline): This pipeline is used when the storage data for a smart contract is too large to be downloaded at once. + It enables the storage data to be downloaded in ranges, similar to the account data. + + 5. Healing Phase: Initiated after all other pipelines have completed their tasks, this phase ensures the integrity and completeness of the downloaded data. + */ final Pipeline> completionPipeline = PipelineBuilder.>createPipeline( "requestDataAvailable", bufferCapacity, outputCounter, true, "node_data_request") diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/StackTrie.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/StackTrie.java index fd6acde3754..01f17eb79b4 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/StackTrie.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/StackTrie.java @@ -51,8 +51,8 @@ public class StackTrie { private final AtomicInteger nbSegments; private final int maxSegments; private final Bytes32 startKeyHash; - private final Map elements; - private final AtomicLong elementsCount; + private Map elements; + private AtomicLong elementsCount; public StackTrie(final Hash rootHash, final Bytes32 startKeyHash) { this(rootHash, 1, 1, startKeyHash); @@ -78,6 +78,12 @@ public void addElement( taskIdentifier, ImmutableTaskElement.builder().proofs(proofs).keys(keys).build()); } + public void removeElement(final Bytes32 taskIdentifier) { + if (this.elements.containsKey(taskIdentifier)) { + this.elementsCount.addAndGet(-this.elements.remove(taskIdentifier).keys().size()); + } + } + public TaskElement getElement(final Bytes32 taskIdentifier) { return this.elements.get(taskIdentifier); } @@ -142,6 +148,11 @@ public void maybeStoreNode(final Bytes location, final Node node) { } } + public void clear() { + this.elements = new LinkedHashMap<>(); + this.elementsCount = new AtomicLong(); + } + public boolean addSegment() { if (nbSegments.get() > maxSegments) { return false; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/AccountRangeDataRequest.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/AccountRangeDataRequest.java index a1a6bc63da4..06181fd09f1 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/AccountRangeDataRequest.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/AccountRangeDataRequest.java @@ -216,6 +216,12 @@ public TreeMap getAccounts() { return stackTrie.getElement(startKeyHash).keys(); } + @Override + public void clear() { + stackTrie.clear(); + isProofValid = Optional.of(false); + } + public Bytes serialize() { return RLP.encode( out -> { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/BytecodeRequest.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/BytecodeRequest.java index 96673d6f874..5db5ec0211e 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/BytecodeRequest.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/BytecodeRequest.java @@ -88,6 +88,11 @@ public Bytes32 getAccountHash() { return accountHash; } + @Override + public void clear() { + setCode(Bytes.EMPTY); + } + public Bytes32 getCodeHash() { return codeHash; } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java index c18d063d74d..14839f0ad6f 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java @@ -205,6 +205,12 @@ public Bytes32 getEndKeyHash() { return endKeyHash; } + @Override + public void clear() { + this.isProofValid = Optional.of(false); + this.stackTrie.removeElement(startKeyHash); + } + @VisibleForTesting public void setProofValid(final boolean isProofValid) { this.isProofValid = Optional.of(isProofValid); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/heal/TrieNodeHealingRequest.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/heal/TrieNodeHealingRequest.java index c04066141d8..ef7191a0167 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/heal/TrieNodeHealingRequest.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/heal/TrieNodeHealingRequest.java @@ -123,6 +123,11 @@ public boolean isResponseReceived() { return !data.isEmpty() && Hash.hash(data).equals(getNodeHash()); } + @Override + public void clear() { + setData(Bytes.EMPTY); + } + @Override public boolean isExpired(final SnapSyncProcessState snapSyncState) { return snapSyncState.isExpired(this); diff --git a/gradle.properties b/gradle.properties index 5e6dc436548..3607a73c5b1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=23.10.1 +version=23.10.2-RC org.gradle.welcome=never # Set exports/opens flags required by Google Java Format and ErrorProne plugins. (JEP-396)