diff --git a/CHANGELOG.md b/CHANGELOG.md index 07cdf586349..d14b3e0fc82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ # Changelog -## Next Release +## Next release + +### Breaking Changes + +### Additions and Improvements + +### Bug fixes + +## 24.7.0 + +### Upcoming Breaking Changes +- Receipt compaction will be enabled by default in a future version of Besu. After this change it will not be possible to downgrade to the previous Besu version. +- PKI-backed QBFT will be removed in a future version of Besu. Other forms of QBFT will remain unchanged. +- --Xbonsai-limit-trie-logs-enabled is deprecated, use --bonsai-limit-trie-logs-enabled instead +- --Xbonsai-trie-logs-pruning-window-size is deprecated, use --bonsai-trie-logs-pruning-window-size instead +- `besu storage x-trie-log` subcommand is deprecated, use `besu storage trie-log` instead ### Breaking Changes - `Xp2p-peer-lower-bound` has been removed. [#7247](https://github.com/hyperledger/besu/pull/7247) @@ -15,6 +30,9 @@ - Update Docker base image to Ubuntu 24.04 [#7251](https://github.com/hyperledger/besu/pull/7251) - Add LUKSO as predefined network name [#7223](https://github.com/hyperledger/besu/pull/7223) - Refactored how code, initcode, and max stack size are configured in forks. [#7245](https://github.com/hyperledger/besu/pull/7245) +- Nodes in a permissioned chain maintain (and retry) connections to bootnodes [#7257](https://github.com/hyperledger/besu/pull/7257) +- Promote experimental `besu storage x-trie-log` subcommand to production-ready [#7278](https://github.com/hyperledger/besu/pull/7278) +- Enhanced BFT round-change diagnostics [#7271](https://github.com/hyperledger/besu/pull/7271) ### Bug fixes - Validation errors ignored in accounts-allowlist and empty list [#7138](https://github.com/hyperledger/besu/issues/7138) @@ -22,6 +40,8 @@ - Fix "Could not confirm best peer had pivot block" [#7109](https://github.com/hyperledger/besu/issues/7109) - Fix "Chain Download Halt" [#6884](https://github.com/hyperledger/besu/issues/6884) + + ## 24.6.0 ### Breaking Changes diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningAcceptanceTest.java index 903919c800c..f1ed4e14902 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningAcceptanceTest.java @@ -38,9 +38,10 @@ public void setUp() { permissionedCluster.start(bootnode, forbiddenNode, allowedNode, permissionedNode); // updating permissioning smart contract with allowed nodes - + permissionedNode.verify(nodeIsForbidden(bootnode)); permissionedNode.execute(allowNode(bootnode)); permissionedNode.verify(nodeIsAllowed(bootnode)); + permissionedNode.verify(admin.hasPeer(bootnode)); permissionedNode.execute(allowNode(allowedNode)); permissionedNode.verify(nodeIsAllowed(allowedNode)); @@ -48,7 +49,6 @@ public void setUp() { permissionedNode.execute(allowNode(permissionedNode)); permissionedNode.verify(nodeIsAllowed(permissionedNode)); - permissionedNode.verify(admin.addPeer(bootnode)); permissionedNode.verify(admin.addPeer(allowedNode)); allowedNode.verify(eth.syncingStatus(false)); diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningV2AcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningV2AcceptanceTest.java index efb88178d6d..1f2cc682c69 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningV2AcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningV2AcceptanceTest.java @@ -62,7 +62,7 @@ public void permissionedNodeShouldPeerOnlyWithAllowedNodes() { @Test public void permissionedNodeShouldDisconnectFromNodeNotPermittedAnymore() { - permissionedNode.verify(admin.addPeer(bootnode)); + permissionedNode.verify(admin.hasPeer(bootnode)); permissionedNode.verify(admin.addPeer(allowedNode)); permissionedNode.verify(net.awaitPeerCount(2)); @@ -74,7 +74,7 @@ public void permissionedNodeShouldDisconnectFromNodeNotPermittedAnymore() { @Test public void permissionedNodeShouldConnectToNewlyPermittedNode() { - permissionedNode.verify(admin.addPeer(bootnode)); + permissionedNode.verify(admin.hasPeer(bootnode)); permissionedNode.verify(admin.addPeer(allowedNode)); permissionedNode.verify(net.awaitPeerCount(2)); @@ -89,7 +89,7 @@ public void permissionedNodeShouldConnectToNewlyPermittedNode() { @Test public void permissioningUpdatesPropagateThroughNetwork() { - permissionedNode.verify(admin.addPeer(bootnode)); + permissionedNode.verify(admin.hasPeer(bootnode)); permissionedNode.verify(admin.addPeer(allowedNode)); permissionedNode.verify(net.awaitPeerCount(2)); diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index d62843049e5..7ed627cfc17 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -792,7 +792,20 @@ public Runner build() { LOG.debug("added ethash observer: {}", stratumServer.get()); } - sanitizePeers(network, staticNodes) + final Stream maintainedPeers; + if (besuController.getGenesisConfigOptions().isPoa()) { + // In a permissioned chain Besu should maintain connections to both static nodes and + // bootnodes, which includes retries periodically + maintainedPeers = + sanitizePeers( + network, + Stream.concat(staticNodes.stream(), bootnodes.stream()).collect(Collectors.toList())); + LOG.debug("Added bootnodes to the maintained peer list"); + } else { + // In a public chain only maintain connections to static nodes + maintainedPeers = sanitizePeers(network, staticNodes); + } + maintainedPeers .map(DefaultPeer::fromEnodeURL) .forEach(peerNetwork::addMaintainedConnectionPeer); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommand.java index 6b0507895c3..de42616822f 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommand.java @@ -49,7 +49,8 @@ /** The Trie Log subcommand. */ @Command( - name = "x-trie-log", + name = "trie-log", + aliases = "x-trie-log", description = "Manipulate trie logs", mixinStandardHelpOptions = true, versionProvider = VersionProvider.class, diff --git a/build.gradle b/build.gradle index a8e02a88356..7d2293b61e3 100644 --- a/build.gradle +++ b/build.gradle @@ -365,9 +365,63 @@ allprojects { options.addBooleanOption('Xdoclint/package:-org.hyperledger.besu.privacy.contracts.generated,' + '-org.hyperledger.besu.tests.acceptance.*,' + '-org.hyperledger.besu.tests.web3j.generated,' + - // TODO: these are temporary disabled (ethereum and sub modules), it should be removed in a future PR. - '-org.hyperledger.besu.ethereum.*,' + - '-org.hyperledger.besu.evmtool', + // TODO: these are temporary excluded from lint (ethereum sub modules), it should be removed in a future PR. + // ethereum api module + '-org.hyperledger.besu.ethereum.api.handlers,' + + '-org.hyperledger.besu.ethereum.api.jsonrpc,' + + '-org.hyperledger.besu.ethereum.api.jsonrpc.*,' + + '-org.hyperledger.besu.ethereum.api.query,' + + '-org.hyperledger.besu.ethereum.api.query.*,' + + '-org.hyperledger.besu.ethereum.api.tls,' + + '-org.hyperledger.besu.ethereum.api.util,' + + // ethereum blockcreation module + '-org.hyperledger.besu.ethereum.blockcreation,' + + '-org.hyperledger.besu.ethereum.blockcreation.*,' + + // ethereum core module + '-org.hyperledger.besu.ethereum.chain,' + + '-org.hyperledger.besu.ethereum.core,' + + '-org.hyperledger.besu.ethereum.core.*,' + + '-org.hyperledger.besu.ethereum.debug,' + + '-org.hyperledger.besu.ethereum.difficulty.fixed,' + + '-org.hyperledger.besu.ethereum.forkid,' + + '-org.hyperledger.besu.ethereum.mainnet,' + + '-org.hyperledger.besu.ethereum.mainnet.*,' + + '-org.hyperledger.besu.ethereum.privacy,' + + '-org.hyperledger.besu.ethereum.privacy.*,' + + '-org.hyperledger.besu.ethereum.processing,' + + '-org.hyperledger.besu.ethereum.proof,' + + '-org.hyperledger.besu.ethereum.storage,' + + '-org.hyperledger.besu.ethereum.storage.*,' + + '-org.hyperledger.besu.ethereum.transaction,' + + '-org.hyperledger.besu.ethereum.trie.*,' + + '-org.hyperledger.besu.ethereum.util,' + + '-org.hyperledger.besu.ethereum.vm,' + + '-org.hyperledger.besu.ethereum.worldstate,' + + // ethereum eth module + '-org.hyperledger.besu.ethereum.eth.*,' + + '-org.hyperledger.besu.ethereum.eth,' + + '-org.hyperledger.besu.consensus.merge,' + + // p2p module + '-org.hyperledger.besu.ethereum.p2p,' + + '-org.hyperledger.besu.ethereum.p2p.*,' + + // permissioning module + '-org.hyperledger.besu.ethereum.permissioning,' + + '-org.hyperledger.besu.ethereum.permissioning.*,' + + // referencetests module + '-org.hyperledger.besu.ethereum.referencetests,' + + // retesteth module + '-org.hyperledger.besu.ethereum.retesteth.methods,' + + '-org.hyperledger.besu.ethereum.retesteth,' + + //rlp module + '-org.hyperledger.besu.ethereum.rlp,' + + // stratum module + '-org.hyperledger.besu.ethereum.stratum,' + + // trie module + '-org.hyperledger.besu.ethereum.trie.*,' + + '-org.hyperledger.besu.ethereum.trie,' + + // verkle trie module + '-org.hyperledger.besu.ethereum.verkletrie,' + + '-org.hyperledger.besu.ethereum.verkletrie.*', true) options.addStringOption('Xmaxerrs','65535') options.addStringOption('Xmaxwarns','65535') diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/RoundTimer.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/RoundTimer.java index a500a1ad7ee..6a8b02991d6 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/RoundTimer.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/RoundTimer.java @@ -20,8 +20,14 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** Class for starting and keeping organised round timers */ public class RoundTimer { + + private static final Logger LOG = LoggerFactory.getLogger(RoundTimer.class); + private final BftExecutors bftExecutors; private Optional> currentTimerTask; private final BftEventQueue queue; @@ -71,6 +77,16 @@ public synchronized void startTimer(final ConsensusRoundIdentifier round) { final ScheduledFuture newTimerTask = bftExecutors.scheduleTask(newTimerRunnable, expiryTime, TimeUnit.MILLISECONDS); + + // Once we are up to round 2 start logging round expiries + if (round.getRoundNumber() >= 2) { + LOG.info( + "QBFT round {} expired. Moved to round {} which will expire in {} seconds", + round.getRoundNumber() - 1, + round.getRoundNumber(), + (expiryTime / 1000)); + } + currentTimerTask = Optional.of(newTimerTask); } } diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManager.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManager.java index 0940d35df6b..f79537cc0f9 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManager.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManager.java @@ -274,17 +274,26 @@ private

> void actionOrBufferMessage( @Override public void handleRoundChangePayload(final RoundChange message) { final ConsensusRoundIdentifier targetRound = message.getRoundIdentifier(); - LOG.trace("Received a RoundChange Payload for {}", targetRound); + + LOG.debug( + "Round change from {}: block {}, round {}", + message.getAuthor(), + message.getRoundIdentifier().getSequenceNumber(), + message.getRoundIdentifier().getRoundNumber()); + + // Diagnostic logging (only logs anything if the chain has stalled) + roundChangeManager.storeAndLogRoundChangeSummary(message); final MessageAge messageAge = determineAgeOfPayload(message.getRoundIdentifier().getRoundNumber()); if (messageAge == MessageAge.PRIOR_ROUND) { - LOG.trace("Received RoundChange Payload for a prior round. targetRound={}", targetRound); + LOG.debug("Received RoundChange Payload for a prior round. targetRound={}", targetRound); return; } final Optional> result = roundChangeManager.appendRoundChangeMessage(message); + if (result.isPresent()) { LOG.debug( "Received sufficient RoundChange messages to change round to targetRound={}", diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerFactory.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerFactory.java index 0d306b1fc9f..889f83f98a7 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerFactory.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerFactory.java @@ -86,7 +86,8 @@ private BaseQbftBlockHeightManager createFullBlockHeightManager(final BlockHeade new RoundChangeManager( BftHelpers.calculateRequiredValidatorQuorum(finalState.getValidators().size()), messageValidatorFactory.createRoundChangeMessageValidator( - parentHeader.getNumber() + 1L, parentHeader)), + parentHeader.getNumber() + 1L, parentHeader), + finalState.getLocalAddress()), roundFactory, finalState.getClock(), messageValidatorFactory, diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/RoundChangeManager.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/RoundChangeManager.java index ca671a6f527..89e2888395c 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/RoundChangeManager.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/statemachine/RoundChangeManager.java @@ -100,19 +100,51 @@ public Collection createRoundChangeCertificate() { @VisibleForTesting final Map roundChangeCache = Maps.newHashMap(); + /** A summary of the latest round each validator is on, for diagnostic purposes only */ + private final Map roundSummary = Maps.newHashMap(); + private final long quorum; private final RoundChangeMessageValidator roundChangeMessageValidator; + private final Address localAddress; /** * Instantiates a new Round change manager. * * @param quorum the quorum * @param roundChangeMessageValidator the round change message validator + * @param localAddress this node's address */ public RoundChangeManager( - final long quorum, final RoundChangeMessageValidator roundChangeMessageValidator) { + final long quorum, + final RoundChangeMessageValidator roundChangeMessageValidator, + final Address localAddress) { this.quorum = quorum; this.roundChangeMessageValidator = roundChangeMessageValidator; + this.localAddress = localAddress; + } + + /** + * Store the latest round for a node, and if chain is stalled log a summary of which round each + * address is on + * + * @param message the round-change message that has just been received + */ + public void storeAndLogRoundChangeSummary(final RoundChange message) { + roundSummary.put(message.getAuthor(), message.getRoundIdentifier()); + if (roundChangeCache.keySet().stream() + .findFirst() + .orElse(new ConsensusRoundIdentifier(0, 0)) + .getRoundNumber() + >= 2) { + LOG.info("BFT round summary (quorum = {})", quorum); + for (Map.Entry nextEntry : roundSummary.entrySet()) { + LOG.info( + "Address: {} Round: {} {}", + nextEntry.getKey(), + nextEntry.getValue().getRoundNumber(), + nextEntry.getKey().equals(localAddress) ? "(Local node)" : ""); + } + } } /** diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java index 334b716900e..e317845f841 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java @@ -18,58 +18,125 @@ import org.immutables.value.Value; +/** + * The ApiConfiguration class provides configuration for the API. It includes default values for gas + * price, max logs range, gas cap, and other parameters. + */ @Value.Immutable @Value.Style(allParameters = true) public abstract class ApiConfiguration { + /** + * The default lower bound coefficient for gas and priority fee. This value is used as the default + * lower bound when calculating the gas and priority fee. + */ public static final long DEFAULT_LOWER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT = 0L; + + /** + * The default upper bound coefficient for gas and priority fee. This value is used as the default + * upper bound when calculating the gas and priority fee. + */ public static final long DEFAULT_UPPER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT = Long.MAX_VALUE; + /** Constructs a new ApiConfiguration with default values. */ + protected ApiConfiguration() {} + + /** + * Returns the number of blocks to consider for gas price calculations. Default value is 100. + * + * @return the number of blocks for gas price calculations + */ @Value.Default public long getGasPriceBlocks() { return 100; } + /** + * Returns the percentile to use for gas price calculations. Default value is 50.0. + * + * @return the percentile for gas price calculations + */ @Value.Default public double getGasPricePercentile() { return 50.0d; } + /** + * Returns the maximum gas price. Default value is 500 GWei. + * + * @return the maximum gas price + */ @Value.Default public Wei getGasPriceMax() { return Wei.of(500_000_000_000L); // 500 GWei } + /** + * Returns the fraction to use for gas price calculations. This is derived from the gas price + * percentile. + * + * @return the fraction for gas price calculations + */ @Value.Derived public double getGasPriceFraction() { return getGasPricePercentile() / 100.0; } + /** + * Returns the maximum range for logs. Default value is 5000. + * + * @return the maximum range for logs + */ @Value.Default public Long getMaxLogsRange() { return 5000L; } + /** + * Returns the gas cap. Default value is 0. + * + * @return the gas cap + */ @Value.Default public Long getGasCap() { return 0L; } + /** + * Returns whether gas and priority fee limiting is enabled. Default value is false. + * + * @return true if gas and priority fee limiting is enabled, false otherwise + */ @Value.Default public boolean isGasAndPriorityFeeLimitingEnabled() { return false; } + /** + * Returns the lower bound coefficient for gas and priority fee. Default value is 0. + * + * @return the lower bound coefficient for gas and priority fee + */ @Value.Default public Long getLowerBoundGasAndPriorityFeeCoefficient() { return DEFAULT_LOWER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT; } + /** + * Returns the upper bound coefficient for gas and priority fee. Default value is Long.MAX_VALUE. + * + * @return the upper bound coefficient for gas and priority fee + */ @Value.Default public Long getUpperBoundGasAndPriorityFeeCoefficient() { return DEFAULT_UPPER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT; } + /** + * Returns the maximum range for trace filter. Default value is 1000. + * + * @return the maximum range for trace filter + */ @Value.Default public Long getMaxTraceFilterRange() { return 1000L; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLConfiguration.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLConfiguration.java index 8c32b552889..a2829edb071 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLConfiguration.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLConfiguration.java @@ -26,17 +26,33 @@ import com.google.common.base.MoreObjects; +/** + * Represents the configuration for GraphQL. This class is used to set and get the configuration + * details for GraphQL such as enabling GraphQL, setting the port and host, setting the allowed + * domains for CORS, setting the hosts allowlist, and setting the HTTP timeout. + */ public class GraphQLConfiguration { private static final String DEFAULT_GRAPHQL_HTTP_HOST = "127.0.0.1"; + + /** The default port number for the GraphQL HTTP server. */ public static final int DEFAULT_GRAPHQL_HTTP_PORT = 8547; private boolean enabled; private int port; private String host; private List corsAllowedDomains = Collections.emptyList(); - private List hostsAllowlist = Arrays.asList("localhost", "127.0.0.1"); + private List hostsAllowlist = Arrays.asList("localhost", DEFAULT_GRAPHQL_HTTP_HOST); private long httpTimeoutSec = TimeoutOptions.defaultOptions().getTimeoutSeconds(); + /** + * Creates a default configuration for GraphQL. + * + *

This method initializes a new GraphQLConfiguration object with default settings. The default + * settings are: - GraphQL is not enabled - The port is set to the default GraphQL HTTP port - The + * host is set to the default GraphQL HTTP host - The HTTP timeout is set to the default timeout + * + * @return a GraphQLConfiguration object with default settings + */ public static GraphQLConfiguration createDefault() { final GraphQLConfiguration config = new GraphQLConfiguration(); config.setEnabled(false); @@ -48,52 +64,112 @@ public static GraphQLConfiguration createDefault() { private GraphQLConfiguration() {} + /** + * Checks if GraphQL is enabled. + * + * @return true if GraphQL is enabled, false otherwise + */ public boolean isEnabled() { return enabled; } + /** + * Sets the enabled status of GraphQL. + * + * @param enabled the status to set. true to enable GraphQL, false to disable it + */ public void setEnabled(final boolean enabled) { this.enabled = enabled; } + /** + * Retrieves the port number for the GraphQL HTTP server. + * + * @return the port number + */ public int getPort() { return port; } + /** + * Sets the port number for the GraphQL HTTP server. + * + * @param port the port number to set + */ public void setPort(final int port) { this.port = port; } + /** + * Retrieves the host for the GraphQL HTTP server. + * + * @return the host + */ public String getHost() { return host; } + /** + * Sets the host for the GraphQL HTTP server. + * + * @param host the host to set + */ public void setHost(final String host) { this.host = host; } + /** + * Retrieves the allowed domains for CORS. + * + * @return a collection of allowed domains for CORS + */ Collection getCorsAllowedDomains() { return corsAllowedDomains; } + /** + * Sets the allowed domains for CORS. + * + * @param corsAllowedDomains a list of allowed domains for CORS + */ public void setCorsAllowedDomains(final List corsAllowedDomains) { checkNotNull(corsAllowedDomains); this.corsAllowedDomains = corsAllowedDomains; } + /** + * Retrieves the hosts allowlist. + * + * @return a collection of hosts in the allowlist + */ Collection getHostsAllowlist() { return Collections.unmodifiableCollection(this.hostsAllowlist); } + /** + * Sets the hosts allowlist. + * + * @param hostsAllowlist a list of hosts to be added to the allowlist + */ public void setHostsAllowlist(final List hostsAllowlist) { checkNotNull(hostsAllowlist); this.hostsAllowlist = hostsAllowlist; } + /** + * Retrieves the HTTP timeout in seconds. + * + * @return the HTTP timeout in seconds + */ public Long getHttpTimeoutSec() { return httpTimeoutSec; } + /** + * Sets the HTTP timeout in seconds. + * + * @param httpTimeoutSec the HTTP timeout to set in seconds + */ public void setHttpTimeoutSec(final long httpTimeoutSec) { this.httpTimeoutSec = httpTimeoutSec; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLContextType.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLContextType.java index 05752f813b6..c12f7578c6c 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLContextType.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLContextType.java @@ -14,14 +14,33 @@ */ package org.hyperledger.besu.ethereum.api.graphql; -/** Internal GraphQL Context */ +/** + * Enum representing various context types for GraphQL. + * + *

These context types are used internally by GraphQL to manage different aspects of the system. + */ public enum GraphQLContextType { + /** Represents blockchain queries context. */ BLOCKCHAIN_QUERIES, + + /** Represents protocol schedule context. */ PROTOCOL_SCHEDULE, + + /** Represents transaction pool context. */ TRANSACTION_POOL, + + /** Represents mining coordinator context. */ MINING_COORDINATOR, + + /** Represents synchronizer context. */ SYNCHRONIZER, + + /** Represents is alive handler context. */ IS_ALIVE_HANDLER, + + /** Represents chain ID context. */ CHAIN_ID, + + /** Represents gas cap context. */ GAS_CAP } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetcherContext.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetcherContext.java index 0ccd8d2b1e1..acebe95d119 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetcherContext.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetcherContext.java @@ -21,18 +21,56 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +/** + * Interface representing the context for a GraphQL data fetcher. + * + *

This context provides access to various components of the system such as the transaction pool, + * blockchain queries, mining coordinator, synchronizer, and protocol schedule. + */ public interface GraphQLDataFetcherContext { + /** + * Retrieves the transaction pool. + * + * @return the transaction pool + */ TransactionPool getTransactionPool(); + /** + * Retrieves the blockchain queries. + * + * @return the blockchain queries + */ BlockchainQueries getBlockchainQueries(); + /** + * Retrieves the mining coordinator. + * + * @return the mining coordinator + */ MiningCoordinator getMiningCoordinator(); + /** + * Retrieves the synchronizer. + * + * @return the synchronizer + */ Synchronizer getSynchronizer(); + /** + * Retrieves the protocol schedule. + * + * @return the protocol schedule + */ ProtocolSchedule getProtocolSchedule(); + /** + * Retrieves the is alive handler. + * + *

By default, this method returns a new IsAliveHandler instance with a status of true. + * + * @return the is alive handler + */ default IsAliveHandler getIsAliveHandler() { return new IsAliveHandler(true); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetcherContextImpl.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetcherContextImpl.java index 0e426ebf3ab..1f5ed82d749 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetcherContextImpl.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetcherContextImpl.java @@ -21,6 +21,12 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +/** + * Implementation of the GraphQLDataFetcherContext interface. + * + *

This class provides access to various components of the system such as the transaction pool, + * blockchain queries, mining coordinator, synchronizer, and protocol schedule. + */ public class GraphQLDataFetcherContextImpl implements GraphQLDataFetcherContext { private final BlockchainQueries blockchainQueries; @@ -30,6 +36,12 @@ public class GraphQLDataFetcherContextImpl implements GraphQLDataFetcherContext private final TransactionPool transactionPool; private final IsAliveHandler isAliveHandler; + /** + * Constructor that takes a GraphQLDataFetcherContext and an IsAliveHandler. + * + * @param context the GraphQLDataFetcherContext + * @param isAliveHandler the IsAliveHandler + */ public GraphQLDataFetcherContextImpl( final GraphQLDataFetcherContext context, final IsAliveHandler isAliveHandler) { this( @@ -41,6 +53,16 @@ public GraphQLDataFetcherContextImpl( isAliveHandler); } + /** + * Constructor that takes a BlockchainQueries, ProtocolSchedule, TransactionPool, + * MiningCoordinator, and Synchronizer. + * + * @param blockchainQueries the BlockchainQueries + * @param protocolSchedule the ProtocolSchedule + * @param transactionPool the TransactionPool + * @param miningCoordinator the MiningCoordinator + * @param synchronizer the Synchronizer + */ public GraphQLDataFetcherContextImpl( final BlockchainQueries blockchainQueries, final ProtocolSchedule protocolSchedule, @@ -56,6 +78,17 @@ public GraphQLDataFetcherContextImpl( new IsAliveHandler(true)); } + /** + * Constructor that takes a BlockchainQueries, ProtocolSchedule, TransactionPool, + * MiningCoordinator, Synchronizer, and IsAliveHandler. + * + * @param blockchainQueries the BlockchainQueries + * @param protocolSchedule the ProtocolSchedule + * @param transactionPool the TransactionPool + * @param miningCoordinator the MiningCoordinator + * @param synchronizer the Synchronizer + * @param isAliveHandler the IsAliveHandler + */ public GraphQLDataFetcherContextImpl( final BlockchainQueries blockchainQueries, final ProtocolSchedule protocolSchedule, diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java index 312491f5c36..0b97bdb2e91 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java @@ -61,10 +61,34 @@ import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +/** + * This class contains data fetchers for GraphQL queries. + * + *

Data fetchers are responsible for fetching data for a specific field. Each field in the schema + * is associated with a data fetcher. When the field is being processed during a query, the + * associated data fetcher is invoked to get the data for that field. + * + *

This class contains data fetchers for various fields such as protocol version, syncing state, + * pending state, gas price, chain ID, max priority fee per gas, range block, block, account, logs, + * and transaction. + * + *

Each data fetcher is a method that returns a `DataFetcher` object. The `DataFetcher` object + * defines how to fetch the data for the field. It takes a `DataFetchingEnvironment` object as input + * which contains all the context needed to fetch the data. + */ public class GraphQLDataFetchers { private final Integer highestEthVersion; + /** + * Constructs a new GraphQLDataFetchers instance. + * + *

This constructor takes a set of supported capabilities and determines the highest Ethereum + * protocol version supported by these capabilities. This version is then stored and can be + * fetched using the getProtocolVersionDataFetcher method. + * + * @param supportedCapabilities a set of capabilities supported by the Ethereum node + */ public GraphQLDataFetchers(final Set supportedCapabilities) { final OptionalInt version = supportedCapabilities.stream() @@ -74,10 +98,30 @@ public GraphQLDataFetchers(final Set supportedCapabilities) { highestEthVersion = version.isPresent() ? version.getAsInt() : null; } + /** + * Returns a DataFetcher that fetches the highest Ethereum protocol version supported by the node. + * + *

The DataFetcher is a functional interface. It has a single method that takes a + * DataFetchingEnvironment object as input and returns the highest Ethereum protocol version as an + * Optional. + * + * @return a DataFetcher that fetches the highest Ethereum protocol version + */ DataFetcher> getProtocolVersionDataFetcher() { return dataFetchingEnvironment -> Optional.of(highestEthVersion); } + /** + * Returns a DataFetcher that fetches the result of sending a raw transaction. + * + *

The DataFetcher is a functional interface. It has a single method that takes a + * DataFetchingEnvironment object as input and returns the hash of the transaction if it is valid + * and added to the transaction pool. If the transaction is invalid, it throws a GraphQLException + * with the invalid reason. If the raw transaction data cannot be read, it throws a + * GraphQLException with INVALID_PARAMS error. + * + * @return a DataFetcher that fetches the result of sending a raw transaction + */ DataFetcher> getSendRawTransactionDataFetcher() { return dataFetchingEnvironment -> { try { @@ -99,6 +143,19 @@ DataFetcher> getSendRawTransactionDataFetcher() { }; } + /** + * Returns a DataFetcher that fetches the syncing state of the Ethereum node. + * + *

The DataFetcher is a functional interface. It has a single method that takes a + * DataFetchingEnvironment object as input and returns the syncing state as an + * Optional. + * + *

The SyncStateAdapter is a wrapper around the SyncStatus of the Ethereum node. It provides + * information about the current syncing state of the node such as the current block, highest + * block, and starting block. + * + * @return a DataFetcher that fetches the syncing state of the Ethereum node + */ DataFetcher> getSyncingDataFetcher() { return dataFetchingEnvironment -> { final Synchronizer synchronizer = @@ -125,6 +182,15 @@ DataFetcher getGasPriceDataFetcher() { }; } + /** + * Returns a DataFetcher that fetches the chain ID of the Ethereum node. + * + *

The DataFetcher is a functional interface. It has a single method that takes a + * DataFetchingEnvironment object as input and returns the chain ID as an {@code + * Optional}. + * + * @return a DataFetcher that fetches the chain ID of the Ethereum node + */ public DataFetcher> getChainIdDataFetcher() { return dataFetchingEnvironment -> { final GraphQLContext graphQLContext = dataFetchingEnvironment.getGraphQlContext(); @@ -132,6 +198,15 @@ public DataFetcher> getChainIdDataFetcher() { }; } + /** + * Returns a DataFetcher that fetches the maximum priority fee per gas of the Ethereum node. + * + *

The DataFetcher is a functional interface. It has a single method that takes a + * DataFetchingEnvironment object as input and returns the maximum priority fee per gas as a Wei + * object. If the maximum priority fee per gas is not available, it returns Wei.ZERO. + * + * @return a DataFetcher that fetches the maximum priority fee per gas of the Ethereum node + */ public DataFetcher getMaxPriorityFeePerGasDataFetcher() { return dataFetchingEnvironment -> { final BlockchainQueries blockchainQuery = @@ -167,6 +242,20 @@ DataFetcher> getRangeBlockDataFetcher() { }; } + /** + * Returns a DataFetcher that fetches a specific block in the Ethereum blockchain. + * + *

The DataFetcher is a functional interface. It has a single method that takes a + * DataFetchingEnvironment object as input. This method fetches a block based on either a block + * number or a block hash. If both a block number and a block hash are provided, it throws a + * GraphQLException with INVALID_PARAMS error. If neither a block number nor a block hash is + * provided, it fetches the latest block. + * + *

The fetched block is then wrapped in a {@link NormalBlockAdapter} and returned as an {@code + * Optional}. + * + * @return a DataFetcher that fetches a specific block in the Ethereum blockchain + */ public DataFetcher> getBlockDataFetcher() { return dataFetchingEnvironment -> { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLHttpService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLHttpService.java index abbcdbf945a..fc763a1e3ba 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLHttpService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLHttpService.java @@ -68,6 +68,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * This class handles the HTTP service for GraphQL. It sets up the server, handles requests and + * responses, and manages the lifecycle of the server. + * + *

It is responsible for processing GraphQL requests, executing them using the provided GraphQL + * engine, and returning the results in the HTTP response. + * + *

It also handles errors and exceptions that may occur during the processing of a request. + */ public class GraphQLHttpService { private static final Logger LOG = LoggerFactory.getLogger(GraphQLHttpService.class); @@ -126,6 +135,15 @@ private void validateConfig(final GraphQLConfiguration config) { checkArgument(config.getHost() != null, "Required host is not configured."); } + /** + * Starts the GraphQL HTTP service. + * + *

This method initializes the HTTP server and sets up the necessary routes for handling + * GraphQL requests. It also validates the configuration and sets up the necessary handlers for + * different types of requests. + * + * @return a CompletableFuture that will be completed when the server is successfully started. + */ public CompletableFuture start() { LOG.info("Starting GraphQL HTTP service on {}:{}", config.getHost(), config.getPort()); // Create the HTTP server and a router object. @@ -230,6 +248,14 @@ private boolean hostIsInAllowlist(final String hostHeader) { } } + /** + * Stops the GraphQL HTTP service. + * + *

This method stops the HTTP server that was created and started by the start() method. If the + * server is not running, this method will do nothing. + * + * @return a CompletableFuture that will be completed when the server is successfully stopped. + */ public CompletableFuture stop() { if (httpServer == null) { return CompletableFuture.completedFuture(null); @@ -248,6 +274,15 @@ public CompletableFuture stop() { return resultFuture; } + /** + * Returns the socket address of the GraphQL HTTP service. + * + *

This method returns the socket address that the HTTP server is bound to. If the server is + * not running, it returns an empty socket address. + * + * @return the socket address of the HTTP server, or an empty socket address if the server is not + * running. + */ public InetSocketAddress socketAddress() { if (httpServer == null) { return EMPTY_SOCKET_ADDRESS; @@ -255,6 +290,14 @@ public InetSocketAddress socketAddress() { return new InetSocketAddress(config.getHost(), httpServer.actualPort()); } + /** + * Returns the URL of the GraphQL HTTP service. + * + *

This method constructs and returns the URL that the HTTP server is bound to. If the server + * is not running, it returns an empty string. + * + * @return the URL of the HTTP server, or an empty string if the server is not running. + */ @VisibleForTesting public String url() { if (httpServer == null) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLProvider.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLProvider.java index 7e62e39c057..2a3370572b4 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLProvider.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLProvider.java @@ -31,12 +31,30 @@ import graphql.schema.idl.TypeDefinitionRegistry; import graphql.schema.idl.TypeRuntimeWiring; +/** + * This class provides the GraphQL service. + * + *

It contains a method to build the GraphQL service with the provided data fetchers. + */ public class GraphQLProvider { + /** + * The maximum complexity allowed for a GraphQL query. + * + *

This constant is used to prevent overly complex queries from being executed. A query's + * complexity is calculated based on the number and type of fields it contains. + */ public static final int MAX_COMPLEXITY = 200; private GraphQLProvider() {} + /** + * Builds the GraphQL service with the provided data fetchers. + * + * @param graphQLDataFetchers the data fetchers to be used in the GraphQL service. + * @return the built GraphQL service. + * @throws IOException if there is an error reading the schema file. + */ public static GraphQL buildGraphQL(final GraphQLDataFetchers graphQLDataFetchers) throws IOException { final URL url = Resources.getResource("schema.graphqls"); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/Scalars.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/Scalars.java index 5762215bc57..9fbeae5fde5 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/Scalars.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/Scalars.java @@ -35,6 +35,13 @@ import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; +/** + * The Scalars class provides methods for creating GraphQLScalarType objects. These objects + * represent the scalar types used in GraphQL, such as Address, BigInt, Bytes, Bytes32, and Long. + * Each method in this class returns a GraphQLScalarType object that has been configured with a + * specific Coercing implementation. The Coercing implementation defines how that type is + * serialized, deserialized and validated. + */ public class Scalars { private Scalars() {} @@ -366,6 +373,14 @@ public Number parseLiteral( } }; + /** + * Creates a new GraphQLScalarType object for an Address. + * + *

The object is configured with a specific Coercing implementation that defines how the + * Address type is serialized, deserialized and validated. + * + * @return a GraphQLScalarType object for an Address. + */ public static GraphQLScalarType addressScalar() { return GraphQLScalarType.newScalar() .name("Address") @@ -374,6 +389,14 @@ public static GraphQLScalarType addressScalar() { .build(); } + /** + * Creates a new GraphQLScalarType object for a BigInt. + * + *

The object is configured with a specific Coercing implementation that defines how the BigInt + * type is serialized, deserialized and validated. + * + * @return a GraphQLScalarType object for a BigInt. + */ public static GraphQLScalarType bigIntScalar() { return GraphQLScalarType.newScalar() .name("BigInt") @@ -382,6 +405,14 @@ public static GraphQLScalarType bigIntScalar() { .build(); } + /** + * Creates a new GraphQLScalarType object for Bytes. + * + *

The object is configured with a specific Coercing implementation that defines how the Bytes + * type is serialized, deserialized and validated. + * + * @return a GraphQLScalarType object for Bytes. + */ public static GraphQLScalarType bytesScalar() { return GraphQLScalarType.newScalar() .name("Bytes") @@ -390,6 +421,14 @@ public static GraphQLScalarType bytesScalar() { .build(); } + /** + * Creates a new GraphQLScalarType object for Bytes32. + * + *

The object is configured with a specific Coercing implementation that defines how the + * Bytes32 type is serialized, deserialized and validated. + * + * @return a GraphQLScalarType object for Bytes32. + */ public static GraphQLScalarType bytes32Scalar() { return GraphQLScalarType.newScalar() .name("Bytes32") @@ -398,6 +437,14 @@ public static GraphQLScalarType bytes32Scalar() { .build(); } + /** + * Creates a new GraphQLScalarType object for a Long. + * + *

The object is configured with a specific Coercing implementation that defines how the Long + * type is serialized, deserialized and validated. + * + * @return a GraphQLScalarType object for a Long. + */ public static GraphQLScalarType longScalar() { return GraphQLScalarType.newScalar() .name("Long") diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/AccessListEntryAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/AccessListEntryAdapter.java index 10451dcc35a..a0dc8743c8e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/AccessListEntryAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/AccessListEntryAdapter.java @@ -23,19 +23,40 @@ import org.apache.tuweni.bytes.Bytes32; +/** + * The AccessListEntryAdapter class extends the AdapterBase class. It provides methods to get the + * storage keys and address from an AccessListEntry. + */ @SuppressWarnings("unused") // reflected by GraphQL public class AccessListEntryAdapter extends AdapterBase { + + /** The AccessListEntry object that this adapter wraps. */ private final AccessListEntry accessListEntry; + /** + * Constructs a new AccessListEntryAdapter with the given AccessListEntry. + * + * @param accessListEntry the AccessListEntry to be adapted + */ public AccessListEntryAdapter(final AccessListEntry accessListEntry) { this.accessListEntry = accessListEntry; } + /** + * Returns the storage keys from the AccessListEntry. + * + * @return a list of storage keys + */ public List getStorageKeys() { final var storage = accessListEntry.storageKeys(); return new ArrayList<>(storage); } + /** + * Returns the address from the AccessListEntry. + * + * @return an Optional containing the address if it exists, otherwise an empty Optional + */ public Optional

getAddress() { return Optional.of(accessListEntry.address()); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/AccountAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/AccountAdapter.java index faf42aafc8e..1b4c8323b64 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/AccountAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/AccountAdapter.java @@ -28,6 +28,10 @@ import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; +/** + * The AccountAdapter class extends the AdapterBase class. It provides methods to get the account + * details such as address, balance, transaction count, code, and storage. + */ @SuppressWarnings("unused") // reflected by GraphQL public class AccountAdapter extends AdapterBase { @@ -35,18 +39,42 @@ public class AccountAdapter extends AdapterBase { private final Address address; private final Optional blockNumber; + /** + * Constructs a new AccountAdapter with the given Account. + * + * @param account the Account to be adapted + */ public AccountAdapter(final Account account) { this(account == null ? null : account.getAddress(), account, Optional.empty()); } + /** + * Constructs a new AccountAdapter with the given Account and block number. + * + * @param account the Account to be adapted + * @param blockNumber the block number associated with the account + */ public AccountAdapter(final Account account, final Optional blockNumber) { this(account == null ? null : account.getAddress(), account, blockNumber); } + /** + * Constructs a new AccountAdapter with the given address and Account. + * + * @param address the address of the account + * @param account the Account to be adapted + */ public AccountAdapter(final Address address, final Account account) { this(address, account, Optional.empty()); } + /** + * Constructs a new AccountAdapter with the given address, Account, and block number. + * + * @param address the address of the account + * @param account the Account to be adapted + * @param blockNumber the block number associated with the account + */ public AccountAdapter( final Address address, final Account account, final Optional blockNumber) { this.account = Optional.ofNullable(account); @@ -54,18 +82,39 @@ public AccountAdapter( this.blockNumber = blockNumber; } + /** + * Returns the address of the account. + * + * @return the address of the account + */ public Address getAddress() { return address; } + /** + * Returns the balance of the account. + * + * @return the balance of the account + */ public Wei getBalance() { return account.map(AccountState::getBalance).orElse(Wei.ZERO); } + /** + * Returns the transaction count of the account. + * + * @return the transaction count of the account + */ public Long getTransactionCount() { return account.map(AccountState::getNonce).orElse(0L); } + /** + * Returns the code of the account. + * + * @param environment the DataFetchingEnvironment + * @return the code of the account + */ public Bytes getCode(final DataFetchingEnvironment environment) { if (account.get() instanceof BonsaiAccount) { @@ -80,6 +129,12 @@ public Bytes getCode(final DataFetchingEnvironment environment) { } } + /** + * Returns the storage of the account. + * + * @param environment the DataFetchingEnvironment + * @return the storage of the account + */ public Bytes32 getStorage(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final Bytes32 slot = environment.getArgument("slot"); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java index 96434ee3750..6d5b696353a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java @@ -48,15 +48,30 @@ import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; +/** + * The BlockAdapterBase class extends the AdapterBase class. It provides methods to get and + * manipulate block data. + */ @SuppressWarnings("unused") // reflected by GraphQL public class BlockAdapterBase extends AdapterBase { private final BlockHeader header; + /** + * Constructs a new BlockAdapterBase with the given BlockHeader. + * + * @param header the BlockHeader to be adapted + */ BlockAdapterBase(final BlockHeader header) { this.header = header; } + /** + * Returns the parent block of the current block. + * + * @param environment the DataFetchingEnvironment + * @return an Optional containing the parent block if it exists, otherwise an empty Optional + */ public Optional getParent(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final Hash parentHash = header.getParentHash(); @@ -65,28 +80,59 @@ public Optional getParent(final DataFetchingEnvironment envi return block.map(NormalBlockAdapter::new); } + /** + * Returns the hash of the current block. + * + * @return the hash of the block + */ public Bytes32 getHash() { return header.getHash(); } + /** + * Returns the nonce of the current block. + * + * @return the nonce of the block + */ public Bytes getNonce() { final long nonce = header.getNonce(); final byte[] bytes = Longs.toByteArray(nonce); return Bytes.wrap(bytes); } + /** + * Returns the transactions root of the current block. + * + * @return the transactions root of the block + */ public Bytes32 getTransactionsRoot() { return header.getTransactionsRoot(); } + /** + * Returns the state root of the current block. + * + * @return the state root of the block + */ public Bytes32 getStateRoot() { return header.getStateRoot(); } + /** + * Returns the receipts root of the current block. + * + * @return the receipts root of the block + */ public Bytes32 getReceiptsRoot() { return header.getReceiptsRoot(); } + /** + * Returns the miner of the current block. + * + * @param environment the DataFetchingEnvironment + * @return an AccountAdapter instance representing the miner of the block + */ public AccountAdapter getMiner(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); @@ -102,46 +148,103 @@ public AccountAdapter getMiner(final DataFetchingEnvironment environment) { .orElseGet(() -> new EmptyAccountAdapter(header.getCoinbase())); } + /** + * Returns the extra data of the current block. + * + * @return the extra data of the block + */ public Bytes getExtraData() { return header.getExtraData(); } + /** + * Returns the base fee per gas of the current block. + * + * @return the base fee per gas of the block + */ public Optional getBaseFeePerGas() { return header.getBaseFee(); } + /** + * Returns the gas limit of the current block. + * + * @return the gas limit of the block + */ public Long getGasLimit() { return header.getGasLimit(); } + /** + * Returns the gas used by the current block. + * + * @return the gas used by the block + */ public Long getGasUsed() { return header.getGasUsed(); } + /** + * Returns the timestamp of the current block. + * + * @return the timestamp of the block + */ public Long getTimestamp() { return header.getTimestamp(); } + /** + * Returns the logs bloom of the current block. + * + * @return the logs bloom of the block + */ public Bytes getLogsBloom() { return header.getLogsBloom(); } + /** + * Returns the mix hash of the current block. + * + * @return the mix hash of the block + */ public Bytes32 getMixHash() { return header.getMixHash(); } + /** + * Returns the difficulty of the current block. + * + * @return the difficulty of the block + */ public Difficulty getDifficulty() { return header.getDifficulty(); } + /** + * Returns the ommer hash of the current block. + * + * @return the ommer hash of the block + */ public Bytes32 getOmmerHash() { return header.getOmmersHash(); } + /** + * Returns the number of the current block. + * + * @return the number of the block + */ public Long getNumber() { return header.getNumber(); } + /** + * Returns an AccountAdapter instance for a given address at the current block. + * + * @param environment the DataFetchingEnvironment + * @return an AccountAdapter instance representing the account of the given address at the current + * block + */ public AccountAdapter getAccount(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final long bn = header.getNumber(); @@ -152,6 +255,13 @@ public AccountAdapter getAccount(final DataFetchingEnvironment environment) { .get(); } + /** + * Returns a list of logs for the current block that match a given filter. + * + * @param environment the DataFetchingEnvironment + * @return a list of LogAdapter instances representing the logs of the current block that match + * the filter + */ public List getLogs(final DataFetchingEnvironment environment) { final Map filter = environment.getArgument("filter"); @@ -183,11 +293,24 @@ public List getLogs(final DataFetchingEnvironment environment) { return results; } + /** + * Estimates the gas used for a call execution. + * + * @param environment the DataFetchingEnvironment + * @return the estimated gas used for the call execution + */ public Long getEstimateGas(final DataFetchingEnvironment environment) { final Optional result = executeCall(environment); return result.map(CallResult::getGasUsed).orElse(0L); } + /** + * Executes a call and returns the result. + * + * @param environment the DataFetchingEnvironment + * @return an Optional containing the result of the call execution if it exists, otherwise an + * empty Optional + */ public Optional getCall(final DataFetchingEnvironment environment) { return executeCall(environment); } @@ -259,12 +382,23 @@ private Optional executeCall(final DataFetchingEnvironment environme header); } + /** + * Returns the raw header of the current block. + * + * @return the raw header of the block + */ Bytes getRawHeader() { final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); header.writeTo(rlpOutput); return rlpOutput.encoded(); } + /** + * Returns the raw data of the current block. + * + * @param environment the DataFetchingEnvironment + * @return the raw data of the block + */ Bytes getRaw(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); return query @@ -279,10 +413,22 @@ Bytes getRaw(final DataFetchingEnvironment environment) { .orElse(Bytes.EMPTY); } + /** + * Returns the withdrawals root of the current block. + * + * @return an Optional containing the withdrawals root if it exists, otherwise an empty Optional + */ Optional getWithdrawalsRoot() { return header.getWithdrawalsRoot().map(Function.identity()); } + /** + * Returns a list of withdrawals for the current block. + * + * @param environment the DataFetchingEnvironment + * @return an Optional containing a list of WithdrawalAdapter instances representing the + * withdrawals of the current block if they exist, otherwise an empty Optional + */ Optional> getWithdrawals(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); return query @@ -295,10 +441,22 @@ Optional> getWithdrawals(final DataFetchingEnvironment e .map(wl -> wl.stream().map(WithdrawalAdapter::new).toList())); } + /** + * Returns the blob gas used by the current block. + * + * @return an Optional containing the blob gas used by the current block if it exists, otherwise + * an empty Optional + */ public Optional getBlobGasUsed() { return header.getBlobGasUsed(); } + /** + * Returns the excess blob gas of the current block. + * + * @return an Optional containing the excess blob gas of the current block if it exists, otherwise + * an empty Optional + */ public Optional getExcessBlobGas() { return header.getExcessBlobGas().map(BlobGas::toLong); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/CallResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/CallResult.java index 78fa30d8748..0a5c9a8f38f 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/CallResult.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/CallResult.java @@ -16,26 +16,55 @@ import org.apache.tuweni.bytes.Bytes; +/** + * Represents the result of a call execution. This class is used to encapsulate the status, gas + * used, and data returned by a call execution. It is used in conjunction with the {@link + * org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.BlockAdapterBase} class. + * + * @see org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.BlockAdapterBase + */ @SuppressWarnings("unused") // reflected by GraphQL public class CallResult { private final Long status; private final Long gasUsed; private final Bytes data; + /** + * Constructs a new CallResult. + * + * @param status the status of the call execution + * @param gasUsed the amount of gas used by the call + * @param data the data returned by the call + */ CallResult(final Long status, final Long gasUsed, final Bytes data) { this.status = status; this.gasUsed = gasUsed; this.data = data; } + /** + * Returns the status of the call execution. + * + * @return the status of the call execution + */ public Long getStatus() { return status; } + /** + * Returns the amount of gas used by the call. + * + * @return the amount of gas used by the call + */ public Long getGasUsed() { return gasUsed; } + /** + * Returns the data returned by the call. + * + * @return the data returned by the call + */ public Bytes getData() { return data; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/EmptyAccountAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/EmptyAccountAdapter.java index 4c19b997b6e..775934fb1ba 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/EmptyAccountAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/EmptyAccountAdapter.java @@ -21,9 +21,22 @@ import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; +/** + * Represents an empty account in the Ethereum blockchain. This class is used when an account does + * not exist at a specific address. It provides default values for the account's properties. It + * extends the {@link org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.AccountAdapter} + * class. + * + * @see org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.AccountAdapter + */ public class EmptyAccountAdapter extends AccountAdapter { private final Address address; + /** + * Constructs a new EmptyAccountAdapter. + * + * @param address the address of the account + */ public EmptyAccountAdapter(final Address address) { super(null); this.address = address; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java index 6beab46fdc8..012edbe512a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java @@ -28,27 +28,62 @@ import graphql.schema.DataFetchingEnvironment; import org.apache.tuweni.bytes.Bytes; +/** + * This class is an adapter for the LogWithMetadata class. + * + *

It extends the AdapterBase class and provides methods to get the index, topics, data, + * transaction, and account associated with a log. + */ @SuppressWarnings("unused") // reflected by GraphQL public class LogAdapter extends AdapterBase { private final LogWithMetadata logWithMetadata; + /** + * Constructor for LogAdapter. + * + *

It initializes the logWithMetadata field with the provided argument. + * + * @param logWithMetadata the log with metadata to be adapted. + */ public LogAdapter(final LogWithMetadata logWithMetadata) { this.logWithMetadata = logWithMetadata; } + /** + * Returns the index of the log. + * + * @return the index of the log. + */ public Integer getIndex() { return logWithMetadata.getLogIndex(); } + /** + * Returns the topics of the log. + * + * @return a list of topics of the log. + */ public List getTopics() { final List topics = logWithMetadata.getTopics(); return new ArrayList<>(topics); } + /** + * Returns the data of the log. + * + * @return the data of the log. + */ public Bytes getData() { return logWithMetadata.getData(); } + /** + * Returns the transaction associated with the log. + * + * @param environment the data fetching environment. + * @return a TransactionAdapter for the transaction associated with the log. + * @throws java.util.NoSuchElementException if the transaction is not found. + */ public TransactionAdapter getTransaction(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final Hash hash = logWithMetadata.getTransactionHash(); @@ -56,6 +91,12 @@ public TransactionAdapter getTransaction(final DataFetchingEnvironment environme return tran.map(TransactionAdapter::new).orElseThrow(); } + /** + * Returns the account associated with the log. + * + * @param environment the data fetching environment. + * @return an AccountAdapter for the account associated with the log. + */ public AccountAdapter getAccount(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); long blockNumber = logWithMetadata.getBlockNumber(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/NormalBlockAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/NormalBlockAdapter.java index f54b3f0da04..fa75028a383 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/NormalBlockAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/NormalBlockAdapter.java @@ -27,9 +27,24 @@ import graphql.schema.DataFetchingEnvironment; +/** + * This class is an adapter for the BlockWithMetadata class. + * + *

It extends the BlockAdapterBase class and provides methods to get the transaction count, total + * difficulty, ommer count, ommers, transactions, and specific ommer and transaction at a given + * index associated with a block. + */ @SuppressWarnings("unused") // reflected by GraphQL public class NormalBlockAdapter extends BlockAdapterBase { + /** + * Constructor for NormalBlockAdapter. + * + *

It initializes the blockWithMetaData field with the provided argument and calls the parent + * constructor with the header of the provided blockWithMetaData. + * + * @param blockWithMetaData the block with metadata to be adapted. + */ public NormalBlockAdapter( final BlockWithMetadata blockWithMetaData) { super(blockWithMetaData.getHeader()); @@ -38,18 +53,39 @@ public NormalBlockAdapter( private final BlockWithMetadata blockWithMetaData; + /** + * Returns the transaction count of the block. + * + * @return the transaction count of the block. + */ public Optional getTransactionCount() { return Optional.of(blockWithMetaData.getTransactions().size()); } + /** + * Returns the total difficulty of the block. + * + * @return the total difficulty of the block. + */ public Difficulty getTotalDifficulty() { return blockWithMetaData.getTotalDifficulty(); } + /** + * Returns the ommer count of the block. + * + * @return the ommer count of the block. + */ public Optional getOmmerCount() { return Optional.of(blockWithMetaData.getOmmers().size()); } + /** + * Returns the ommers of the block. + * + * @param environment the data fetching environment. + * @return a list of UncleBlockAdapter for the ommers of the block. + */ public List getOmmers(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final List ommers = blockWithMetaData.getOmmers(); @@ -63,6 +99,12 @@ public List getOmmers(final DataFetchingEnvironment environme return results; } + /** + * Returns the ommer at a given index of the block. + * + * @param environment the data fetching environment. + * @return an UncleBlockAdapter for the ommer at the given index of the block. + */ public Optional getOmmerAt(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final int index = ((Number) environment.getArgument("index")).intValue(); @@ -75,6 +117,13 @@ public Optional getOmmerAt(final DataFetchingEnvironment envi return Optional.empty(); } + /** + * Returns a list of TransactionAdapter objects for the transactions in the block. + * + *

Each TransactionAdapter object is created by adapting a TransactionWithMetadata object. + * + * @return a list of TransactionAdapter objects for the transactions in the block. + */ public List getTransactions() { final List trans = blockWithMetaData.getTransactions(); final List results = new ArrayList<>(); @@ -84,6 +133,16 @@ public List getTransactions() { return results; } + /** + * Returns a TransactionAdapter object for the transaction at the given index in the block. + * + *

The index is retrieved from the data fetching environment. If there is no transaction at the + * given index, an empty Optional is returned. + * + * @param environment the data fetching environment. + * @return an Optional containing a TransactionAdapter object for the transaction at the given + * index in the block, or an empty Optional if there is no transaction at the given index. + */ public Optional getTransactionAt(final DataFetchingEnvironment environment) { final int index = ((Number) environment.getArgument("index")).intValue(); final List trans = blockWithMetaData.getTransactions(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java index 0202db88f62..49f6bc890f9 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java @@ -36,19 +36,41 @@ import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; +/** + * This class represents the pending state of transactions in the Ethereum network. + * + *

It extends the AdapterBase class and provides methods to interact with the transaction pool. + */ @SuppressWarnings("unused") // reflected by GraphQL public class PendingStateAdapter extends AdapterBase { private final TransactionPool transactionPool; + /** + * Constructor for PendingStateAdapter. + * + *

It initializes the transactionPool field with the provided argument. + * + * @param transactionPool the transaction pool to be used. + */ public PendingStateAdapter(final TransactionPool transactionPool) { this.transactionPool = transactionPool; } + /** + * Returns the count of transactions in the transaction pool. + * + * @return the count of transactions in the transaction pool. + */ public Integer getTransactionCount() { return transactionPool.count(); } + /** + * Returns a list of TransactionAdapter objects for the transactions in the transaction pool. + * + * @return a list of TransactionAdapter objects for the transactions in the transaction pool. + */ public List getTransactions() { return transactionPool.getPendingTransactions().stream() .map(PendingTransaction::getTransaction) @@ -57,9 +79,17 @@ public List getTransactions() { .toList(); } - // until the miner can expose the current "proposed block" we have no - // speculative environment, so estimate against latest. + /** + * Returns an AccountAdapter object for the account associated with the provided address. + * + *

The account state is estimated against the latest block. + * + * @param dataFetchingEnvironment the data fetching environment. + * @return an AccountAdapter object for the account associated with the provided address. + */ public AccountAdapter getAccount(final DataFetchingEnvironment dataFetchingEnvironment) { + // until the miner can expose the current "proposed block" we have no + // speculative environment, so estimate against latest. final BlockchainQueries blockchainQuery = dataFetchingEnvironment.getGraphQlContext().get(GraphQLContextType.BLOCKCHAIN_QUERIES); final Address addr = dataFetchingEnvironment.getArgument("address"); @@ -71,16 +101,39 @@ public AccountAdapter getAccount(final DataFetchingEnvironment dataFetchingEnvir .orElseGet(() -> new AccountAdapter(null)); } - // until the miner can expose the current "proposed block" we have no - // speculative environment, so estimate against latest. + /** + * Estimates the gas required for a transaction. + * + *

This method calls the getCall method to simulate the transaction and then retrieves the gas + * used by the transaction. The gas estimation is done against the latest block as there is + * currently no way to expose the current "proposed block" for a speculative environment. + * + * @param environment the data fetching environment. + * @return an Optional containing the estimated gas for the transaction, or an empty Optional if + * the transaction simulation was not successful. + */ public Optional getEstimateGas(final DataFetchingEnvironment environment) { + // until the miner can expose the current "proposed block" we have no + // speculative environment, so estimate against latest. final Optional result = getCall(environment); return result.map(CallResult::getGasUsed); } - // until the miner can expose the current "proposed block" we have no - // speculative environment, so estimate against latest. + /** + * Simulates the execution of a transaction. + * + *

This method retrieves the transaction parameters from the data fetching environment, creates + * a CallParameter object, and then uses a TransactionSimulator to simulate the execution of the + * transaction. The simulation is done against the latest block as there is currently no way to + * expose the current "proposed block" for a speculative environment. + * + * @param environment the data fetching environment. + * @return an Optional containing the result of the transaction simulation, or an empty Optional + * if the transaction simulation was not successful. + */ public Optional getCall(final DataFetchingEnvironment environment) { + // until the miner can expose the current "proposed block" we have no + // speculative environment, so estimate against latest. final Map callData = environment.getArgument("data"); final Address from = (Address) callData.get("from"); final Address to = (Address) callData.get("to"); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/SyncStateAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/SyncStateAdapter.java index 0a8dcbbf97e..2c3876fe367 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/SyncStateAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/SyncStateAdapter.java @@ -16,22 +16,52 @@ import org.hyperledger.besu.plugin.data.SyncStatus; +/** + * The SyncStateAdapter class provides methods to retrieve the synchronization status of the + * blockchain. + * + *

This class is used to adapt a SyncStatus object into a format that can be used by GraphQL. The + * SyncStatus object is provided at construction time. + * + *

The class provides methods to retrieve the starting block, current block, and highest block of + * the synchronization status. + */ @SuppressWarnings("unused") // reflected by GraphQL public class SyncStateAdapter { private final SyncStatus syncStatus; + /** + * Constructs a new SyncStateAdapter object. + * + * @param syncStatus the SyncStatus object to adapt. + */ public SyncStateAdapter(final SyncStatus syncStatus) { this.syncStatus = syncStatus; } + /** + * Returns the starting block of the synchronization status. + * + * @return the starting block of the synchronization status. + */ public Long getStartingBlock() { return syncStatus.getStartingBlock(); } + /** + * Returns the current block of the synchronization status. + * + * @return the current block of the synchronization status. + */ public Long getCurrentBlock() { return syncStatus.getCurrentBlock(); } + /** + * Returns the highest block of the synchronization status. + * + * @return the highest block of the synchronization status. + */ public Long getHighestBlock() { return syncStatus.getHighestBlock(); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java index 6e98afc90e5..d5eacf8e357 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java @@ -38,11 +38,28 @@ import graphql.schema.DataFetchingEnvironment; import org.apache.tuweni.bytes.Bytes; +/** + * The TransactionAdapter class provides methods to retrieve the transaction details. + * + *

This class is used to adapt a TransactionWithMetadata object into a format that can be used by + * GraphQL. The TransactionWithMetadata object is provided at construction time. + * + *

The class provides methods to retrieve the hash, type, nonce, index, from, to, value, gas + * price, max fee per gas, max priority fee per gas, max fee per blob gas, effective tip, gas, input + * data, block, status, gas used, cumulative gas used, effective gas price, blob gas used, blob gas + * price, created contract, logs, R, S, V, Y parity, access list, raw, raw receipt, and blob + * versioned hashes of the transaction. + */ @SuppressWarnings("unused") // reflected by GraphQL public class TransactionAdapter extends AdapterBase { private final TransactionWithMetadata transactionWithMetadata; private Optional transactionReceiptWithMetadata; + /** + * Constructs a new TransactionAdapter object. + * + * @param transactionWithMetadata the TransactionWithMetadata object to adapt. + */ public TransactionAdapter(final @Nonnull TransactionWithMetadata transactionWithMetadata) { this.transactionWithMetadata = transactionWithMetadata; } @@ -65,22 +82,53 @@ private Optional getReceipt( return transactionReceiptWithMetadata; } + /** + * Returns the hash of the transaction. + * + * @return the hash of the transaction. + */ public Hash getHash() { return transactionWithMetadata.getTransaction().getHash(); } + /** + * Returns the type of the transaction. + * + * @return the type of the transaction. + */ public Optional getType() { return Optional.of(transactionWithMetadata.getTransaction().getType().ordinal()); } + /** + * Returns the nonce of the transaction. + * + * @return the nonce of the transaction. + */ public Long getNonce() { return transactionWithMetadata.getTransaction().getNonce(); } + /** + * Returns the index of the transaction. + * + * @return the index of the transaction. + */ public Optional getIndex() { return transactionWithMetadata.getTransactionIndex(); } + /** + * Retrieves the sender of the transaction. + * + *

This method uses the BlockchainQueries to get the block number and then retrieves the sender + * of the transaction. It then uses the getAndMapWorldState method to get the state of the + * sender's account at the given block number. + * + * @param environment the data fetching environment. + * @return an AccountAdapter object representing the sender's account state at the given block + * number. + */ public AccountAdapter getFrom(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final Long blockNumber = @@ -96,6 +144,18 @@ public AccountAdapter getFrom(final DataFetchingEnvironment environment) { .orElse(new EmptyAccountAdapter(addr)); } + /** + * Retrieves the recipient of the transaction. + * + *

This method uses the BlockchainQueries to get the block number and then retrieves the + * recipient of the transaction. It then uses the getAndMapWorldState method to get the state of + * the recipient's account at the given block number. + * + * @param environment the data fetching environment. + * @return an Optional containing an AccountAdapter object representing the recipient's account + * state at the given block number, or an empty Optional if the transaction does not have a + * recipient (i.e., it is a contract creation transaction). + */ public Optional getTo(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final Long blockNumber = @@ -115,39 +175,95 @@ public Optional getTo(final DataFetchingEnvironment environment) .or(() -> Optional.of(new EmptyAccountAdapter(address)))); } + /** + * Retrieves the value of the transaction. + * + * @return a Wei object representing the value of the transaction. + */ public Wei getValue() { return transactionWithMetadata.getTransaction().getValue(); } + /** + * Retrieves the gas price of the transaction. + * + * @return a Wei object representing the gas price of the transaction. If the transaction does not + * specify a gas price, this method returns zero. + */ public Wei getGasPrice() { return transactionWithMetadata.getTransaction().getGasPrice().orElse(Wei.ZERO); } + /** + * Retrieves the maximum fee per gas of the transaction. + * + * @return an Optional containing a Wei object representing the maximum fee per gas of the + * transaction, or an empty Optional if the transaction does not specify a maximum fee per + * gas. + */ public Optional getMaxFeePerGas() { return transactionWithMetadata.getTransaction().getMaxFeePerGas(); } + /** + * Retrieves the maximum priority fee per gas of the transaction. + * + * @return an Optional containing a Wei object representing the maximum priority fee per gas of + * the transaction, or an empty Optional if the transaction does not specify a maximum + * priority fee per gas. + */ public Optional getMaxPriorityFeePerGas() { return transactionWithMetadata.getTransaction().getMaxPriorityFeePerGas(); } + /** + * Retrieves the maximum fee per blob gas of the transaction. + * + * @return an Optional containing a Wei object representing the maximum fee per blob gas of the + * transaction, or an empty Optional if the transaction does not specify a maximum fee per + * blob gas. + */ public Optional getMaxFeePerBlobGas() { return transactionWithMetadata.getTransaction().getMaxFeePerBlobGas(); } + /** + * Retrieves the effective tip of the transaction. + * + * @param environment the data fetching environment. + * @return an Optional containing a Wei object representing the effective tip of the transaction, + * or an empty Optional if the transaction does not specify an effective tip. + */ public Optional getEffectiveTip(final DataFetchingEnvironment environment) { return getReceipt(environment) .map(rwm -> rwm.getTransaction().getEffectivePriorityFeePerGas(rwm.getBaseFee())); } + /** + * Retrieves the gas limit of the transaction. + * + * @return a Long object representing the gas limit of the transaction. + */ public Long getGas() { return transactionWithMetadata.getTransaction().getGasLimit(); } + /** + * Retrieves the input data of the transaction. + * + * @return a Bytes object representing the input data of the transaction. + */ public Bytes getInputData() { return transactionWithMetadata.getTransaction().getPayload(); } + /** + * Retrieves the block of the transaction. + * + * @param environment the data fetching environment. + * @return an Optional containing a NormalBlockAdapter object representing the block of the + * transaction, or an empty Optional if the transaction does not specify a block. + */ public Optional getBlock(final DataFetchingEnvironment environment) { return transactionWithMetadata .getBlockHash() @@ -155,6 +271,17 @@ public Optional getBlock(final DataFetchingEnvironment envir .map(NormalBlockAdapter::new); } + /** + * Retrieves the status of the transaction. + * + *

This method uses the getReceipt method to get the receipt of the transaction. It then checks + * the status of the receipt. If the status is -1, it returns an empty Optional. Otherwise, it + * returns an Optional containing the status of the receipt. + * + * @param environment the data fetching environment. + * @return an Optional containing a Long object representing the status of the transaction, or an + * empty Optional if the status of the receipt is -1. + */ public Optional getStatus(final DataFetchingEnvironment environment) { return getReceipt(environment) .map(TransactionReceiptWithMetadata::getReceipt) @@ -165,27 +292,87 @@ public Optional getStatus(final DataFetchingEnvironment environment) { : Optional.of((long) receipt.getStatus())); } + /** + * Retrieves the gas used by the transaction. + * + *

This method uses the getReceipt method to get the receipt of the transaction. It then + * returns an Optional containing the gas used by the transaction. + * + * @param environment the data fetching environment. + * @return an Optional containing a Long object representing the gas used by the transaction. + */ public Optional getGasUsed(final DataFetchingEnvironment environment) { return getReceipt(environment).map(TransactionReceiptWithMetadata::getGasUsed); } + /** + * Retrieves the cumulative gas used by the transaction. + * + *

This method uses the getReceipt method to get the receipt of the transaction. It then + * returns an Optional containing the cumulative gas used by the transaction. + * + * @param environment the data fetching environment. + * @return an Optional containing a Long object representing the cumulative gas used by the + * transaction. + */ public Optional getCumulativeGasUsed(final DataFetchingEnvironment environment) { return getReceipt(environment).map(rpt -> rpt.getReceipt().getCumulativeGasUsed()); } + /** + * Retrieves the effective gas price of the transaction. + * + *

This method uses the getReceipt method to get the receipt of the transaction. It then + * returns an Optional containing the effective gas price of the transaction. + * + * @param environment the data fetching environment. + * @return an Optional containing a Wei object representing the effective gas price of the + * transaction. + */ public Optional getEffectiveGasPrice(final DataFetchingEnvironment environment) { return getReceipt(environment) .map(rwm -> rwm.getTransaction().getEffectiveGasPrice(rwm.getBaseFee())); } + /** + * Retrieves the blob gas used by the transaction. + * + *

This method uses the getReceipt method to get the receipt of the transaction. It then + * returns an Optional containing the blob gas used by the transaction. + * + * @param environment the data fetching environment. + * @return an Optional containing a Long object representing the blob gas used by the transaction. + */ public Optional getBlobGasUsed(final DataFetchingEnvironment environment) { return getReceipt(environment).flatMap(TransactionReceiptWithMetadata::getBlobGasUsed); } + /** + * Retrieves the blob gas price of the transaction. + * + *

This method uses the getReceipt method to get the receipt of the transaction. It then + * returns an Optional containing the blob gas price of the transaction. + * + * @param environment the data fetching environment. + * @return an Optional containing a Wei object representing the blob gas price of the transaction. + */ public Optional getBlobGasPrice(final DataFetchingEnvironment environment) { return getReceipt(environment).flatMap(TransactionReceiptWithMetadata::getBlobGasPrice); } + /** + * Retrieves the contract created by the transaction. + * + *

This method checks if the transaction is a contract creation transaction. If it is, it + * retrieves the address of the created contract. It then uses the BlockchainQueries to get the + * block number and then retrieves the state of the created contract's account at the given block + * number. + * + * @param environment the data fetching environment. + * @return an Optional containing an AccountAdapter object representing the created contract's + * account state at the given block number, or an empty Optional if the transaction is not a + * contract creation transaction or if the block number is not specified. + */ public Optional getCreatedContract(final DataFetchingEnvironment environment) { final boolean contractCreated = transactionWithMetadata.getTransaction().isContractCreation(); if (contractCreated) { @@ -208,6 +395,17 @@ public Optional getCreatedContract(final DataFetchingEnvironment return Optional.empty(); } + /** + * Retrieves the logs of the transaction. + * + *

This method uses the BlockchainQueries to get the block header and the receipt of the + * transaction. It then retrieves the logs of the transaction and adapts them into a format that + * can be used by GraphQL. + * + * @param environment the data fetching environment. + * @return a List of LogAdapter objects representing the logs of the transaction. If the + * transaction does not have a receipt, this method returns an empty list. + */ public List getLogs(final DataFetchingEnvironment environment) { final BlockchainQueries query = getBlockchainQueries(environment); final ProtocolSchedule protocolSchedule = @@ -240,14 +438,34 @@ public List getLogs(final DataFetchingEnvironment environment) { return results; } + /** + * Retrieves the R component of the transaction's signature. + * + * @return a BigInteger object representing the R component of the transaction's signature. + */ public BigInteger getR() { return transactionWithMetadata.getTransaction().getR(); } + /** + * Retrieves the S component of the transaction's signature. + * + * @return a BigInteger object representing the S component of the transaction's signature. + */ public BigInteger getS() { return transactionWithMetadata.getTransaction().getS(); } + /** + * Retrieves the V component of the transaction's signature. + * + *

If the transaction type is less than the BLOB transaction type and V is null, it returns the + * Y parity of the transaction. Otherwise, it returns V. + * + * @return an Optional containing a BigInteger object representing the V component of the + * transaction's signature, or an Optional containing the Y parity of the transaction if V is + * null and the transaction type is less than the BLOB transaction type. + */ public Optional getV() { BigInteger v = transactionWithMetadata.getTransaction().getV(); return Optional.ofNullable( @@ -258,10 +476,22 @@ public Optional getV() { : v); } + /** + * Retrieves the Y parity of the transaction's signature. + * + * @return an Optional containing a BigInteger object representing the Y parity of the + * transaction's signature. + */ public Optional getYParity() { return Optional.ofNullable(transactionWithMetadata.getTransaction().getYParity()); } + /** + * Retrieves the access list of the transaction. + * + * @return a List of AccessListEntryAdapter objects representing the access list of the + * transaction. + */ public List getAccessList() { return transactionWithMetadata .getTransaction() @@ -270,12 +500,30 @@ public List getAccessList() { .orElse(List.of()); } + /** + * Retrieves the raw transaction data. + * + *

This method uses the writeTo method of the transaction to write the transaction data to a + * BytesValueRLPOutput object. It then encodes the BytesValueRLPOutput object and returns it. + * + * @return an Optional containing a Bytes object representing the raw transaction data. + */ public Optional getRaw() { final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); transactionWithMetadata.getTransaction().writeTo(rlpOutput); return Optional.of(rlpOutput.encoded()); } + /** + * Retrieves the raw receipt of the transaction. + * + *

This method uses the getReceipt method to get the receipt of the transaction. It then writes + * the receipt data to a BytesValueRLPOutput object using the writeToForNetwork method of the + * receipt. It then encodes the BytesValueRLPOutput object and returns it. + * + * @param environment the data fetching environment. + * @return an Optional containing a Bytes object representing the raw receipt of the transaction. + */ public Optional getRawReceipt(final DataFetchingEnvironment environment) { return getReceipt(environment) .map( @@ -286,6 +534,11 @@ public Optional getRawReceipt(final DataFetchingEnvironment environment) }); } + /** + * Retrieves the versioned hashes of the transaction. + * + * @return a List of VersionedHash objects representing the versioned hashes of the transaction. + */ public List getBlobVersionedHashes() { return transactionWithMetadata.getTransaction().getVersionedHashes().orElse(List.of()); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/WithdrawalAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/WithdrawalAdapter.java index e8454fcdb5b..85ec69b0ddf 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/WithdrawalAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/WithdrawalAdapter.java @@ -17,26 +17,60 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.plugin.data.Withdrawal; +/** + * The WithdrawalAdapter class provides methods to retrieve the withdrawal details. + * + *

This class is used to adapt a Withdrawal object into a format that can be used by GraphQL. The + * Withdrawal object is provided at construction time. + * + *

The class provides methods to retrieve the index, validator, address, and amount of the + * withdrawal. + */ public class WithdrawalAdapter { Withdrawal withdrawal; + /** + * Constructs a new WithdrawalAdapter object. + * + * @param withdrawal the Withdrawal object to adapt. + */ public WithdrawalAdapter(final Withdrawal withdrawal) { this.withdrawal = withdrawal; } + /** + * Returns the index of the withdrawal. + * + * @return the index of the withdrawal. + */ public Long getIndex() { return withdrawal.getIndex().toLong(); } + /** + * Returns the validator of the withdrawal. + * + * @return the validator of the withdrawal. + */ public Long getValidator() { return withdrawal.getValidatorIndex().toLong(); } + /** + * Returns the address of the withdrawal. + * + * @return the address of the withdrawal. + */ public Address getAddress() { return withdrawal.getAddress(); } + /** + * Returns the amount of the withdrawal. + * + * @return the amount of the withdrawal. + */ public Long getAmount() { return withdrawal.getAmount().getAsBigInteger().longValue(); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java index e4aeacd92fd..f370df8dcfb 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLError.java @@ -19,30 +19,96 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonGetter; +/** + * Enum representing various types of GraphQL errors. + * + *

Each error is associated with a specific code and message. The code is an integer that + * uniquely identifies the error. The message is a string that provides a human-readable description + * of the error. + * + *

This enum includes standard errors such as INVALID_PARAMS and INTERNAL_ERROR, transaction + * validation failures such as NONCE_TOO_LOW and INVALID_TRANSACTION_SIGNATURE, and private + * transaction errors such as PRIVATE_TRANSACTION_FAILED and PRIVATE_NONCE_TOO_LOW. + * + *

The {@code of} method is used to map a {@code TransactionInvalidReason} to a corresponding + * {@code GraphQLError}. + */ @JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum GraphQLError { // Standard errors + /** Error code -32602. This error occurs when the parameters provided are invalid. */ INVALID_PARAMS(-32602, "Invalid params"), + + /** Error code -32603. This error occurs when there is an internal error. */ INTERNAL_ERROR(-32603, "Internal error"), // Transaction validation failures + /** Error code -32001. This error occurs when the nonce value is too low. */ NONCE_TOO_LOW(-32001, "Nonce too low"), + + /** Error code -32002. This error occurs when the transaction signature is invalid. */ INVALID_TRANSACTION_SIGNATURE(-32002, "Invalid signature"), + + /** Error code -32003. This error occurs when the intrinsic gas exceeds the gas limit. */ INTRINSIC_GAS_EXCEEDS_LIMIT(-32003, "Intrinsic gas exceeds gas limit"), + + /** + * Error code -32004. This error occurs when the upfront cost of the transaction exceeds the + * account balance. + */ TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE(-32004, "Upfront cost exceeds account balance"), + + /** + * Error code -32005. This error occurs when the transaction gas limit exceeds the block gas + * limit. + */ EXCEEDS_BLOCK_GAS_LIMIT(-32005, "Transaction gas limit exceeds block gas limit"), + + /** Error code -32006. This error occurs when the nonce value is too high. */ INCORRECT_NONCE(-32006, "Nonce too high"), + + /** + * Error code -32007. This error occurs when the sender account is not authorized to send + * transactions. + */ TX_SENDER_NOT_AUTHORIZED(-32007, "Sender account not authorized to send transactions"), + + /** Error code -32008. This error occurs when the initial sync is still in progress. */ CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE(-32008, "Initial sync is still in progress"), + + /** + * Error code -32009. This error occurs when the gas price is below the configured minimum gas + * price. + */ GAS_PRICE_TOO_LOW(-32009, "Gas price below configured minimum gas price"), + + /** + * Error code -32000. This error occurs when the Chain ID in the transaction signature is wrong. + */ WRONG_CHAIN_ID(-32000, "Wrong Chain ID in transaction signature"), + + /** + * Error code -32000. This error occurs when signatures with replay protection are not supported. + */ REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED( -32000, "Signatures with replay protection are not supported"), + + /** Error code -32000. This error occurs when the transaction fee cap is exceeded. */ TX_FEECAP_EXCEEDED(-32000, "Transaction fee cap exceeded"), // Private Transaction Errors + /** Error code -32000. This error occurs when a private transaction fails. */ PRIVATE_TRANSACTION_FAILED(-32000, "Private transaction failed"), + + /** + * Error code -50100. This error occurs when the nonce value for a private transaction is too low. + */ PRIVATE_NONCE_TOO_LOW(-50100, "Private transaction nonce too low"), + + /** + * Error code -50100. This error occurs when the nonce value for a private transaction is + * incorrect. + */ INCORRECT_PRIVATE_NONCE(-50100, "Private transaction nonce is incorrect"); private final int code; @@ -53,49 +119,52 @@ public enum GraphQLError { this.message = message; } + /** + * Returns the error code associated with this GraphQL error. + * + * @return the error code as an integer. + */ @JsonGetter("code") public int getCode() { return code; } + /** + * Returns the error message associated with this GraphQL error. + * + * @return the error message as a string. + */ @JsonGetter("message") public String getMessage() { return message; } + /** + * Maps a {@code TransactionInvalidReason} to a corresponding {@code GraphQLError}. + * + *

This method is used to convert a transaction invalid reason to a GraphQL error, which can be + * used to provide more detailed error information to the client. + * + * @param transactionInvalidReason the transaction invalid reason to be converted. + * @return the corresponding GraphQL error. + */ public static GraphQLError of(final TransactionInvalidReason transactionInvalidReason) { - switch (transactionInvalidReason) { - case WRONG_CHAIN_ID: - return WRONG_CHAIN_ID; - case REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED: - return REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED; - case INVALID_SIGNATURE: - return INVALID_TRANSACTION_SIGNATURE; - case UPFRONT_COST_EXCEEDS_BALANCE: - return TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE; - case NONCE_TOO_LOW: - case PRIVATE_NONCE_TOO_LOW: - return NONCE_TOO_LOW; - case NONCE_TOO_HIGH: - case PRIVATE_NONCE_TOO_HIGH: - return INCORRECT_NONCE; - case INTRINSIC_GAS_EXCEEDS_GAS_LIMIT: - return INTRINSIC_GAS_EXCEEDS_LIMIT; - case EXCEEDS_BLOCK_GAS_LIMIT: - return EXCEEDS_BLOCK_GAS_LIMIT; - case TX_SENDER_NOT_AUTHORIZED: - return TX_SENDER_NOT_AUTHORIZED; - case CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE: - return CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE; + return switch (transactionInvalidReason) { + case WRONG_CHAIN_ID -> WRONG_CHAIN_ID; + case REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED -> REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED; + case INVALID_SIGNATURE -> INVALID_TRANSACTION_SIGNATURE; + case UPFRONT_COST_EXCEEDS_BALANCE -> TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE; + case NONCE_TOO_LOW, PRIVATE_NONCE_TOO_LOW -> NONCE_TOO_LOW; + case NONCE_TOO_HIGH, PRIVATE_NONCE_TOO_HIGH -> INCORRECT_NONCE; + case INTRINSIC_GAS_EXCEEDS_GAS_LIMIT -> INTRINSIC_GAS_EXCEEDS_LIMIT; + case EXCEEDS_BLOCK_GAS_LIMIT -> EXCEEDS_BLOCK_GAS_LIMIT; + case TX_SENDER_NOT_AUTHORIZED -> TX_SENDER_NOT_AUTHORIZED; + case CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE -> CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE; // Private Transaction Invalid Reasons - case PRIVATE_TRANSACTION_FAILED: - return PRIVATE_TRANSACTION_FAILED; - case GAS_PRICE_TOO_LOW: - return GAS_PRICE_TOO_LOW; - case TX_FEECAP_EXCEEDED: - return TX_FEECAP_EXCEEDED; - default: - return INTERNAL_ERROR; - } + case PRIVATE_TRANSACTION_FAILED -> PRIVATE_TRANSACTION_FAILED; + case GAS_PRICE_TOO_LOW -> GAS_PRICE_TOO_LOW; + case TX_FEECAP_EXCEEDED -> TX_FEECAP_EXCEEDED; + default -> INTERNAL_ERROR; + }; } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLErrorResponse.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLErrorResponse.java index faf129311c1..8204fad0759 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLErrorResponse.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLErrorResponse.java @@ -16,12 +16,26 @@ import com.fasterxml.jackson.annotation.JsonIgnore; +/** + * Represents a GraphQL error response. This class extends the GraphQLResponse class and overrides + * the getType method to return ERROR. + */ public class GraphQLErrorResponse extends GraphQLResponse { + /** + * Constructs a new GraphQLErrorResponse with the specified errors. + * + * @param errors the errors to be included in the response. + */ public GraphQLErrorResponse(final Object errors) { super(errors); } + /** + * Returns the type of this GraphQL response. + * + * @return GraphQLResponseType.ERROR, indicating that this is an error response. + */ @Override @JsonIgnore public GraphQLResponseType getType() { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLJsonRequest.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLJsonRequest.java index 4a0755fad38..899e08a7f08 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLJsonRequest.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLJsonRequest.java @@ -19,36 +19,77 @@ import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonSetter; +/** + * This class represents a GraphQL JSON request. + * + *

It contains the query, operation name, and variables for a GraphQL request. The query is a + * string that represents the GraphQL query. The operation name is a string that represents the + * operation to be performed. The variables is a map that contains the variables for the GraphQL + * query. + */ public class GraphQLJsonRequest { private String query; private String operationName; private Map variables; + /** Default constructor for GraphQLJsonRequest. */ + public GraphQLJsonRequest() {} + + /** + * Gets the query of the GraphQL request. + * + * @return the query of the GraphQL request. + */ @JsonGetter public String getQuery() { return query; } + /** + * Sets the query of the GraphQL request. + * + * @param query the query of the GraphQL request. + */ @JsonSetter public void setQuery(final String query) { this.query = query; } + /** + * Gets the operation name of the GraphQL request. + * + * @return the operation name of the GraphQL request. + */ @JsonGetter public String getOperationName() { return operationName; } + /** + * Sets the operation name of the GraphQL request. + * + * @param operationName the operation name of the GraphQL request. + */ @JsonSetter public void setOperationName(final String operationName) { this.operationName = operationName; } + /** + * Gets the variables of the GraphQL request. + * + * @return the variables of the GraphQL request. + */ @JsonGetter public Map getVariables() { return variables; } + /** + * Sets the variables of the GraphQL request. + * + * @param variables the variables of the GraphQL request. + */ @JsonSetter public void setVariables(final Map variables) { this.variables = variables; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLNoResponse.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLNoResponse.java index abc0725107e..2891ed19226 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLNoResponse.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLNoResponse.java @@ -14,8 +14,20 @@ */ package org.hyperledger.besu.ethereum.api.graphql.internal.response; +/** + * This class represents a GraphQL response with no content. + * + *

It extends the GraphQLResponse class and overrides the getType method to return + * GraphQLResponseType.NONE. + */ public class GraphQLNoResponse extends GraphQLResponse { + /** + * Default constructor for GraphQLNoResponse. + * + *

It calls the parent constructor with null as the argument, indicating no content for this + * response. + */ public GraphQLNoResponse() { super(null); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLResponse.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLResponse.java index 21bc17d31a3..66121625493 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLResponse.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLResponse.java @@ -16,15 +16,35 @@ import java.util.Objects; +/** + * Represents a GraphQL response. This abstract class provides a structure for different types of + * GraphQL responses. + */ public abstract class GraphQLResponse { - public abstract GraphQLResponseType getType(); private final Object result; + /** + * Constructs a new GraphQLResponse with the specified result. + * + * @param result the result to be included in the response. + */ GraphQLResponse(final Object result) { this.result = result; } + /** + * Returns the type of this GraphQL response. + * + * @return the type of this GraphQL response as a GraphQLResponseType. + */ + public abstract GraphQLResponseType getType(); + + /** + * Returns the result of this GraphQL response. + * + * @return the result of this GraphQL response as an Object. + */ public Object getResult() { return result; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLResponseType.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLResponseType.java index ddd5170c371..655d5bc11a5 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLResponseType.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLResponseType.java @@ -16,8 +16,15 @@ /** Various types of responses that the JSON-RPC component may produce. */ public enum GraphQLResponseType { + /** Represents a response type where there is no content. */ NONE, + + /** Represents a successful response type. */ SUCCESS, + + /** Represents an error response type. */ ERROR, + + /** Represents an unauthorized response type. */ UNAUTHORIZED } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLSuccessResponse.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLSuccessResponse.java index c5a709c37a5..0721f7d2769 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLSuccessResponse.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/response/GraphQLSuccessResponse.java @@ -16,12 +16,33 @@ import com.fasterxml.jackson.annotation.JsonIgnore; +/** + * This class represents a successful GraphQL response. + * + *

It extends the GraphQLResponse class and overrides the getType method to return + * GraphQLResponseType.SUCCESS. + */ public class GraphQLSuccessResponse extends GraphQLResponse { + /** + * Constructor for GraphQLSuccessResponse. + * + *

It calls the parent constructor with the provided data as the argument. + * + * @param data the data to be included in the successful response. + */ public GraphQLSuccessResponse(final Object data) { super(data); } + /** + * Returns the type of the GraphQL response. + * + *

This method is overridden to return GraphQLResponseType.SUCCESS, indicating a successful + * response. + * + * @return GraphQLResponseType.SUCCESS + */ @Override @JsonIgnore public GraphQLResponseType getType() { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/WithdrawalRequestParameter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/WithdrawalRequestParameter.java index 67fa13f3f89..3561da37a80 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/WithdrawalRequestParameter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/WithdrawalRequestParameter.java @@ -45,7 +45,7 @@ public static WithdrawalRequestParameter fromWithdrawalRequest( final WithdrawalRequest withdrawalRequest) { return new WithdrawalRequestParameter( withdrawalRequest.getSourceAddress().toHexString(), - withdrawalRequest.getValidatorPublicKey().toHexString(), + withdrawalRequest.getValidatorPubkey().toHexString(), withdrawalRequest.getAmount().toShortHexString()); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WithdrawalRequest.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WithdrawalRequest.java index decdf22c736..0e7afc1595b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WithdrawalRequest.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/WithdrawalRequest.java @@ -26,13 +26,13 @@ public class WithdrawalRequest extends Request implements org.hyperledger.besu.plugin.data.WithdrawalRequest { private final Address sourceAddress; - private final BLSPublicKey validatorPublicKey; + private final BLSPublicKey validatorPubkey; private final GWei amount; public WithdrawalRequest( - final Address sourceAddress, final BLSPublicKey validatorPublicKey, final GWei amount) { + final Address sourceAddress, final BLSPublicKey validatorPubkey, final GWei amount) { this.sourceAddress = sourceAddress; - this.validatorPublicKey = validatorPublicKey; + this.validatorPubkey = validatorPubkey; this.amount = amount; } @@ -47,8 +47,8 @@ public Address getSourceAddress() { } @Override - public PublicKey getValidatorPublicKey() { - return validatorPublicKey; + public PublicKey getValidatorPubkey() { + return validatorPubkey; } @Override @@ -61,8 +61,8 @@ public String toString() { return "WithdrawalRequest{" + "sourceAddress=" + sourceAddress - + " validatorPublicKey=" - + validatorPublicKey + + " validatorPubkey=" + + validatorPubkey + " amount=" + amount + '}'; @@ -78,12 +78,12 @@ public boolean equals(final Object o) { } final WithdrawalRequest that = (WithdrawalRequest) o; return Objects.equals(sourceAddress, that.sourceAddress) - && Objects.equals(validatorPublicKey, that.validatorPublicKey) + && Objects.equals(validatorPubkey, that.validatorPubkey) && Objects.equals(amount, that.amount); } @Override public int hashCode() { - return Objects.hash(sourceAddress, validatorPublicKey, amount); + return Objects.hash(sourceAddress, validatorPubkey, amount); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestDecoder.java index 7ade9947b44..031209284e8 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestDecoder.java @@ -28,11 +28,11 @@ public class WithdrawalRequestDecoder { public static WithdrawalRequest decode(final RLPInput rlpInput) { rlpInput.enterList(); final Address sourceAddress = Address.readFrom(rlpInput); - final BLSPublicKey validatorPublicKey = BLSPublicKey.readFrom(rlpInput); + final BLSPublicKey validatorPubkey = BLSPublicKey.readFrom(rlpInput); final GWei amount = GWei.of(rlpInput.readUInt64Scalar()); rlpInput.leaveList(); - return new WithdrawalRequest(sourceAddress, validatorPublicKey, amount); + return new WithdrawalRequest(sourceAddress, validatorPubkey, amount); } public static WithdrawalRequest decodeOpaqueBytes(final Bytes input) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestEncoder.java index 26be22d4882..c9fdb37fcea 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/WithdrawalRequestEncoder.java @@ -48,7 +48,7 @@ private static void encodeWithdrawalRequest( final WithdrawalRequest withdrawalRequest, final RLPOutput rlpOutput) { rlpOutput.startList(); rlpOutput.writeBytes(withdrawalRequest.getSourceAddress()); - rlpOutput.writeBytes(withdrawalRequest.getValidatorPublicKey()); + rlpOutput.writeBytes(withdrawalRequest.getValidatorPubkey()); rlpOutput.writeUInt64Scalar(withdrawalRequest.getAmount()); rlpOutput.endList(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java index 6bb0e81a75e..c0b7302e867 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java @@ -153,7 +153,7 @@ private static List peekExpectedWithdrawalRequests( queueHeadIndex.plus(i).multiply(WITHDRAWAL_REQUEST_STORAGE_SLOT_SIZE)); final Address sourceAddress = Address.wrap(account.getStorageValue(queueStorageSlot).toBytes().slice(12, 20)); - final BLSPublicKey validatorPublicKey = + final BLSPublicKey validatorPubkey = BLSPublicKey.wrap( Bytes.concatenate( account @@ -165,7 +165,7 @@ private static List peekExpectedWithdrawalRequests( UInt64.fromBytes(account.getStorageValue(queueStorageSlot.plus(2)).slice(16, 8)); withdrawalRequests.add( - new WithdrawalRequest(sourceAddress, validatorPublicKey, GWei.of(amount))); + new WithdrawalRequest(sourceAddress, validatorPubkey, GWei.of(amount))); } return withdrawalRequests; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java index 634a7441596..b6b38b37d57 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java @@ -36,7 +36,7 @@ public class PragueBlockHashProcessor extends CancunBlockHashProcessor { private static final Logger LOG = LoggerFactory.getLogger(PragueBlockHashProcessor.class); public static final Address HISTORY_STORAGE_ADDRESS = - Address.fromHexString("0x25a219378dad9b3503c8268c9ca836a52427a4fb"); + Address.fromHexString("0x0aae40965e6800cd9b1f4b05ff21581047e3f91e"); /** The HISTORY_SERVE_WINDOW */ public static final long HISTORY_SERVE_WINDOW = 8192; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java index 6d21744e7bd..7e1e8571350 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java @@ -31,7 +31,6 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.tuweni.bytes.Bytes; @@ -47,12 +46,12 @@ class WithdrawalRequestContractHelperTest { private MutableAccount contract; @BeforeEach - public void setUp() { + void setUp() { worldState = createInMemoryWorldStateArchive().getMutable(); } @Test - public void popWithdrawalRequestsFromQueue_ReadWithdrawalRequestsCorrectly() { + void popWithdrawalRequestsFromQueue_ReadWithdrawalRequestsCorrectly() { final List validatorWithdrawalRequests = List.of(createExit(), createExit(), createExit()); loadContractStorage(worldState, validatorWithdrawalRequests); @@ -64,7 +63,7 @@ public void popWithdrawalRequestsFromQueue_ReadWithdrawalRequestsCorrectly() { } @Test - public void + void popWithdrawalRequestsFromQueue_whenContractCodeIsEmpty_ReturnsEmptyListOfWithdrawalRequests() { // Create account with empty code final WorldUpdater updater = worldState.updater(); @@ -76,10 +75,10 @@ public void popWithdrawalRequestsFromQueue_ReadWithdrawalRequestsCorrectly() { } @Test - public void popWithdrawalRequestsFromQueue_WhenMoreWithdrawalRequests_UpdatesQueuePointers() { + void popWithdrawalRequestsFromQueue_WhenMoreWithdrawalRequests_UpdatesQueuePointers() { // Loading contract with more than 16 WithdrawalRequests final List validatorWithdrawalRequests = - IntStream.range(0, 30).mapToObj(__ -> createExit()).collect(Collectors.toList()); + IntStream.range(0, 30).mapToObj(__ -> createExit()).toList(); loadContractStorage(worldState, validatorWithdrawalRequests); // After loading the contract, the WithdrawalRequests count since last block should match the // size of the list @@ -102,7 +101,7 @@ public void popWithdrawalRequestsFromQueue_WhenMoreWithdrawalRequests_UpdatesQue } @Test - public void popWithdrawalRequestsFromQueue_WhenNoMoreWithdrawalRequests_ZeroQueuePointers() { + void popWithdrawalRequestsFromQueue_WhenNoMoreWithdrawalRequests_ZeroQueuePointers() { final List withdrawalRequests = List.of(createExit(), createExit(), createExit()); loadContractStorage(worldState, withdrawalRequests); @@ -126,7 +125,7 @@ public void popWithdrawalRequestsFromQueue_WhenNoMoreWithdrawalRequests_ZeroQueu } @Test - public void popWithdrawalRequestsFromQueue_WhenNoWithdrawalRequests_DoesNothing() { + void popWithdrawalRequestsFromQueue_WhenNoWithdrawalRequests_DoesNothing() { // Loading contract with 0 WithdrawalRequests loadContractStorage(worldState, List.of()); // After loading storage, we have the WithdrawalRequests count as zero because no @@ -135,7 +134,7 @@ public void popWithdrawalRequestsFromQueue_WhenNoWithdrawalRequests_DoesNothing( final List poppedWithdrawalRequests = WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState); - assertThat(poppedWithdrawalRequests).hasSize(0); + assertThat(poppedWithdrawalRequests).isEmpty(); // Check that queue pointers are correct (head and tail are zero) assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, 0); @@ -186,14 +185,13 @@ private void loadContractStorage( Bytes.fromHexString("0x000000000000000000000000"), request.getSourceAddress()))); // validator_pubkey contract.setStorageValue( - UInt256.valueOf(offset++), - UInt256.fromBytes(request.getValidatorPublicKey().slice(0, 32))); + UInt256.valueOf(offset++), UInt256.fromBytes(request.getValidatorPubkey().slice(0, 32))); contract.setStorageValue( // set public key to slot, with 16 bytes padding on the right UInt256.valueOf(offset++), UInt256.fromBytes( Bytes.concatenate( - request.getValidatorPublicKey().slice(32, 16), + request.getValidatorPubkey().slice(32, 16), request.getAmount().toBytes(), // 8 bytes for amount Bytes.fromHexString("0x0000000000000000")))); } diff --git a/ethereum/core/src/test/resources/org/hyperledger/besu/ethereum/chain/genesis_prague2.json b/ethereum/core/src/test/resources/org/hyperledger/besu/ethereum/chain/genesis_prague2.json index e7bccadac0b..cdb755ace18 100644 --- a/ethereum/core/src/test/resources/org/hyperledger/besu/ethereum/chain/genesis_prague2.json +++ b/ethereum/core/src/test/resources/org/hyperledger/besu/ethereum/chain/genesis_prague2.json @@ -73,8 +73,8 @@ "balance": "0x0", "nonce": "0x1" }, - "25a219378dad9b3503c8268c9ca836a52427a4fb": { - "code": "0x60203611603157600143035f35116029575f356120000143116029576120005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd00", + "0x0aae40965e6800cd9b1f4b05ff21581047e3f91e": { + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500", "balance": "0x0", "nonce": "0x1" }, diff --git a/ethereum/evmtool/README.md b/ethereum/evmtool/README.md index a1a703f9132..50db1066f38 100644 --- a/ethereum/evmtool/README.md +++ b/ethereum/evmtool/README.md @@ -8,13 +8,9 @@ and enclosing data structures. Using EVM Tool in execution-specification-tests ----------------------------------------------- -To use EVM Tool in Execution Spec tests it is recommended that you use -the GraalVM build, as the framework incurs significant startup penalties -for each invocation when run via the Java runtime. - ### Building Execution Tests on macOS -Current as of 24 Jun 2023. +Current as of 26 Jun 2024. MacOS users will typically encounter two problems,one relating to the version of Python used and one relating to zsh. @@ -35,7 +31,7 @@ install python packages needs to escape the brackets pip install -e .\[docs,lint,test\] ``` -An all-in-one script, including homebrew, would look like +An all-in-one script, using homebrew, would look like ```zsh brew install ethereum solidity @@ -43,85 +39,56 @@ git clone https://github.com/ethereum/execution-spec-tests cd execution-spec-tests python3 -m venv ./venv/ source ./venv/bin/activate -pip install -e .[docs,lint,test] +pip install -e .\[docs,lint,test\] ``` -### Building EvmTool with GraalVM on macOS +### Building EvmTool on macOS -First you need a GraalVM JDK installed, if not already installed. +First you need a Java 21+ JDK installed, if not already installed. It is recommended you install [SDKMAN](https://sdkman.io/install) to -manage the graalVM install, homebrew has issues with native attributes -and code signing. +manage the jvm install. ```zsh -sdk install java 22.3.r17-grl -sdk use java 22.3.r17-grl +sdk install java 21.0.3-tem +sdk use java 21.0.3-tem ``` -You can also manually install GraalVM from -the [GraalVM website](https://www.graalvm.org/downloads).. - -Once GraalVM is installed you use the `nativeCompile` target. +Once a JVM is installed you use the gradle target: ```zsh -./gradlew nativeCompile +./gradlew installDist -x test ``` The resulting binary -is `./ethereum/evmtool/build/native/nativeCompile/evmtool` +is `build/install/besu/bin/evmtool` If the testing repository and besu are installed in the same parent directory, the command to run the execution tests is ```zsh -fill -v tests --evm-bin ../besu/ethereum/evmtool/build/install/evmtool/bin/evm +fill -v tests --evm-bin ../besu/build/install/besu/bin/evmtool ``` Assuming homebrew and SDKMan are both installed, the complete script is ```zsh -sdk install java 22.3.r17-grl -sdk use java 22.3.r17-grl +sdk install java 21.0.3-tem +sdk use java 21.0.3-tem git clone https://github.com/hyperledger/besu cd besu -./gradlew nativeCompile +./gradlew installDist -x test cd .. brew install ethereum solidity +solc-select install latest +solc-select use latest git clone https://github.com/ethereum/execution-spec-tests cd execution-spec-tests python3 -m venv ./venv/ source ./venv/bin/activate -pip install -e .[docs,lint,test] +pip install -e .\[docs,lint,test\] -fill -v tests --evm-bin ../besu/ethereum/evmtool/build/install/evmtool/bin/evm +fill -v tests --evm-bin ../besu/build/install/besu/bin/evmtool ``` -If you don't want to use the GraalVM tool the binary that is compatible -is generated by the `ethereum:evmtool:installdist` target and is located -at `./ethereum/evmtool/build/install/evmtool/bin/evm` - -Why not GraalVM for everything? -------------------------------- - -Using GraalVM in execution-spec-tests results in over 20x performance -increase in execution. It will be faster to build GraalVM from scratch -and run the execution-spec-tests than to run just the Java version. - -It is demonstrably faster to run the Java version for a node. -All the test execution gains are the result of reduced startup -penalties. Larger benchmarks will show that performance intensive EVM -code will be slower in GraalVM than the Java version due to the adaptive -compiler. - -For contracts that execute 30 million gas in small operations it is -often faster to run the Java EVMTool than the GraalVM EVMTool, including -startup penalty. The execution tests focus on smaller VM tests that -demonstrate specification conformance. - -We would also need to reconsider some library choices. GraalVM does not -work with Log4J, and we would have to ban that library across Besu and -all dependents. Libraries such as Netty also have some problematic entry -points that interact poorly with how SLF4J needs to initialize. - diff --git a/ethereum/evmtool/build.gradle b/ethereum/evmtool/build.gradle index 0d1700082fa..aaddc354d5f 100644 --- a/ethereum/evmtool/build.gradle +++ b/ethereum/evmtool/build.gradle @@ -72,16 +72,6 @@ dependencies { mainClassName = 'org.hyperledger.besu.evmtool.EvmTool' -startScripts { - applicationName = 'evm' - defaultJvmOpts = [ - "-Dsecp256k1.randomize=false" - ] - doLast { - unixScript.text = unixScript.text.replace('BESU_HOME', '\$APP_HOME') - windowsScript.text = windowsScript.text.replace('BESU_HOME', '%~dp0..') - } -} // rename the top level dir from besu- to besu and this makes it really // simple for use in docker @@ -148,34 +138,3 @@ tasks.register('dockerUpload', Exec) { executable "sh" args "-c", cmd } - -graalvmNative { - binaries { - main { - sharedLibrary = false - buildArgs.addAll( - "-H:ReflectionConfigurationFiles=${projectDir}/src/main/graal/reflection-config.json", - "-H:AdditionalSecurityProviders=org.bouncycastle.jce.provider.BouncyCastleProvider", - '-H:+TraceSecurityServices' - ) - - - // Netty drags in older versions of bouncy castle, exclude it so there are no conflicts - excludeConfig.put("io.netty:netty-buffer", [".*"]) - } - } -} - - -configurations.nativeImageClasspath { - // netty statically allocates some problematic classes - exclude group: 'io.netty', module: 'netty-buffer' - exclude group: 'io.netty', module: 'netty-common' - exclude group: 'io.netty', module: 'netty-transport' - - // keep log4j from sneaking in. GraalVM has an aleric reaction if it sees even one class - exclude group: 'org.slf4j', module: 'log4j-over-slf4j:1.7.36' - exclude group: "log4j", module: "log4j" - exclude group: "org.apache.logging.log4j" - exclude group: 'org.bouncycastle', module: 'bcprov-jdk18on' -} diff --git a/ethereum/evmtool/src/main/docker/Dockerfile b/ethereum/evmtool/src/main/docker/Dockerfile index 9d9bf4aafa1..9d7b48d3cb1 100644 --- a/ethereum/evmtool/src/main/docker/Dockerfile +++ b/ethereum/evmtool/src/main/docker/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update $NO_PROXY_CACHE && \ apt-get -o Acquire::BrokenProxy=true -o Acquire::http::No-Cache=true -o Acquire::http::Pipeline-Depth=0 \ --no-install-recommends -q --assume-yes install ca-certificates-java=20190909* && \ apt-get -o Acquire::BrokenProxy=true -o Acquire::http::No-Cache=true -o Acquire::http::Pipeline-Depth=0 \ - --no-install-recommends -q --assume-yes install openjdk-17-jre-headless=17* && \ + --no-install-recommends -q --assume-yes install openjdk-21-jre-headless=21* && \ # Clean apt cache \ apt-get clean && \ rm -rf /var/cache/apt/archives/* /var/cache/apt/archives/partial/* && \ diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/B11rSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/B11rSubCommand.java index 9a27a8a40d9..0bb7949754f 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/B11rSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/B11rSubCommand.java @@ -51,6 +51,11 @@ import picocli.CommandLine.Parameters; import picocli.CommandLine.ParentCommand; +/** + * This class implements the Runnable interface and represents the B11rSubCommand. It is responsible + * for handling the block builder subcommand in the EVM tool. It provides methods to read headers, + * move fields, and run the command. + */ @Command( name = COMMAND_NAME, aliases = {COMMAND_ALIAS}, @@ -138,12 +143,18 @@ public void consumeParameters( } } + /** Default constructor for the B11rSubCommand class. This is required by PicoCLI. */ @SuppressWarnings("unused") public B11rSubCommand() { // PicoCLI requires this parentCommand = null; } + /** + * Constructs a new B11rSubCommand with the given parent command. + * + * @param parentCommand the parent command of this subcommand + */ @SuppressWarnings("unused") public B11rSubCommand(final EvmToolCommand parentCommand) { // PicoCLI requires this too diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java index 05d90a7b358..829bf2d58e7 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BenchmarkSubCommand.java @@ -33,13 +33,22 @@ import picocli.CommandLine.Parameters; import picocli.CommandLine.ParentCommand; +/** + * This class represents the BenchmarkSubCommand. It is responsible for executing an Ethereum State + * Test. + */ @CommandLine.Command( name = COMMAND_NAME, description = "Execute an Ethereum State Test.", mixinStandardHelpOptions = true, versionProvider = VersionProvider.class) public class BenchmarkSubCommand implements Runnable { + /** + * The command name for the BenchmarkSubCommand. This constant is used as the name attribute in + * the {@code CommandLine.Command} annotation. + */ public static final String COMMAND_NAME = "benchmark"; + private final PrintStream output; enum Benchmark { @@ -68,11 +77,17 @@ enum Benchmark { @ParentCommand EvmToolCommand parentCommand; + /** Default constructor for the BenchmarkSubCommand class. This is required by PicoCLI. */ public BenchmarkSubCommand() { // PicoCLI requires this this(System.out); } + /** + * Constructs a new BenchmarkSubCommand with the given output stream. + * + * @param output the output stream to be used + */ public BenchmarkSubCommand(final PrintStream output) { this.output = output; } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java index b3a1531133a..7b463fe08e3 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainModule.java @@ -39,10 +39,18 @@ import dagger.Provides; import org.apache.tuweni.bytes.Bytes32; +/** + * This class is a Dagger module that provides dependencies related to the blockchain. It includes + * the GenesisFileModule and DataStoreModule for providing the genesis block and data store + * respectively. The class is annotated with {@code @Module} to indicate that it is a Dagger module. + */ @SuppressWarnings("WeakerAccess") @Module(includes = {GenesisFileModule.class, DataStoreModule.class}) public class BlockchainModule { + /** Default constructor for the BlockchainModule class. */ + public BlockchainModule() {} + @Singleton @Provides Blockchain provideBlockchain( diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java index 10e167f4069..e16d4014f0b 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/CodeValidateSubCommand.java @@ -40,6 +40,11 @@ import picocli.CommandLine; import picocli.CommandLine.ParentCommand; +/** + * This class represents the CodeValidateSubCommand. It is responsible for validating EVM code for + * fuzzing. It implements the Runnable interface and is annotated with the {@code + * CommandLine.Command} annotation. + */ @SuppressWarnings({"ConstantValue", "DataFlowIssue"}) @CommandLine.Command( name = COMMAND_NAME, @@ -47,6 +52,10 @@ mixinStandardHelpOptions = true, versionProvider = VersionProvider.class) public class CodeValidateSubCommand implements Runnable { + /** + * The command name for the CodeValidateSubCommand. This constant is used as the name attribute in + * the CommandLine.Command annotation. + */ public static final String COMMAND_NAME = "code-validate"; @ParentCommand EvmToolCommand parentCommand; @@ -62,6 +71,7 @@ public class CodeValidateSubCommand implements Runnable { @CommandLine.Parameters private final List cliCode = new ArrayList<>(); + /** Default constructor for the CodeValidateSubCommand class. This is required by PicoCLI. */ @SuppressWarnings("unused") public CodeValidateSubCommand() { // PicoCLI requires this @@ -111,6 +121,18 @@ private void checkCodeFromBufferedReader(final BufferedReader in) { } } + /** + * This method is responsible for validating the EVM code. It takes a hexadecimal string + * representation of the EVM code as input. The method first converts the hexadecimal string to + * Bytes. It then checks if the code follows the EOF layout. If the layout is valid, it retrieves + * the code from the EVM. If the code is invalid, it returns an error message with the reason for + * the invalidity. If the code is valid, it returns a string with "OK" followed by the hexadecimal + * string representation of each code section. + * + * @param hexCode the hexadecimal string representation of the EVM code + * @return a string indicating whether the code is valid or not, and in case of validity, the + * hexadecimal string representation of each code section + */ public String considerCode(final String hexCode) { Bytes codeBytes; try { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/DataStoreModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/DataStoreModule.java index 28b212024c5..7629b318476 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/DataStoreModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/DataStoreModule.java @@ -38,6 +38,14 @@ import dagger.Module; import dagger.Provides; +/** + * This class is a Dagger module that provides dependencies related to the data store. It includes + * the GenesisFileModule for providing the genesis block. The class is annotated with + * {@code @Module} to indicate that it is a Dagger module. It provides various key-value storages + * such as variables, blockchain, world state, world state preimage, and pruning. The type of + * key-value storage (e.g., rocksdb, memory) can be specified. The class also provides a + * BlockchainStorage which is a prefixed key blockchain storage. + */ @SuppressWarnings({"CloseableProvides"}) @Module(includes = GenesisFileModule.class) public class DataStoreModule { @@ -50,6 +58,9 @@ public class DataStoreModule { List.of(KeyValueSegmentIdentifier.values()), RocksDBMetricsFactory.PUBLIC_ROCKS_DB_METRICS)); + /** Default constructor for the DataStoreModule class. */ + public DataStoreModule() {} + @Provides @Singleton @Named("variables") diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java index ab53b41e1d4..bfa873e61cf 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EOFTestSubCommand.java @@ -45,13 +45,16 @@ import org.apache.tuweni.bytes.Bytes; import picocli.CommandLine; +/** A PicoCli annotated command for running EOF validation reference tests. */ @CommandLine.Command( name = COMMAND_NAME, description = "Runs EOF validation reference tests", mixinStandardHelpOptions = true, versionProvider = VersionProvider.class) public class EOFTestSubCommand implements Runnable { + /** The name of the EOF validation reference test command. */ public static final String COMMAND_NAME = "eof-test"; + @CommandLine.ParentCommand private final EvmToolCommand parentCommand; // picocli does it magically @@ -65,10 +68,16 @@ public class EOFTestSubCommand implements Runnable { EVM evm; String fork = null; + /** Default constructor for the EOFTestSubCommand class. Sets the parent command to null. */ public EOFTestSubCommand() { this(null); } + /** + * Constructor for the EOFTestSubCommand class with a parent command. + * + * @param parentCommand The parent command for this sub command. + */ public EOFTestSubCommand(final EvmToolCommand parentCommand) { this.parentCommand = parentCommand; } @@ -199,6 +208,12 @@ private void executeEOFTest(final String fileName, final MapEvmToolCommand implements the Runnable interface, making it the entrypoint for PicoCLI to + * execute this command. + * + *

The class provides various options for setting up and executing EVM transactions. These + * options include, but are not limited to, setting the gas price, sender address, receiver address, + * and the data to be sent with the transaction. + * + *

Key methods in this class include 'run()' for executing the command, 'execute()' for setting + * up and running the EVM transaction, and 'dumpWorldState()' for outputting the current state of + * the Ethereum world state. + */ @Command( description = "This command evaluates EVM transactions.", abbreviateSynopsis = true, @@ -247,12 +262,22 @@ void setBytes(final String optionValue) { PrintWriter out; InputStream in; + /** + * Default constructor for the EvmToolCommand class. It initializes the input stream with an empty + * byte array and the output stream with the standard output. + */ public EvmToolCommand() { this( new ByteArrayInputStream(new byte[0]), new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8)), true)); } + /** + * Constructor for the EvmToolCommand class with custom input and output streams. + * + * @param in The input stream to be used. + * @param out The output stream to be used. + */ public EvmToolCommand(final InputStream in, final PrintWriter out) { this.in = in; this.out = out; @@ -322,10 +347,21 @@ private static void addForkHelp(final CommandLine subCommandLine) { subCommandLine.setHelpSectionKeys(keys); } + /** + * Returns the fork name provided by the Dagger options. If no fork is provided, it returns the + * name of the default EVM specification version. + * + * @return The fork name. + */ public String getFork() { return daggerOptions.provideFork().orElse(EvmSpecVersion.defaultVersion().getName()); } + /** + * Checks if a fork is provided in the Dagger options. + * + * @return True if a fork is provided, false otherwise. + */ public boolean hasFork() { return daggerOptions.provideFork().isPresent(); } @@ -506,6 +542,13 @@ public void run() { } } + /** + * Dumps the current state of the Ethereum world state to the provided PrintWriter. The state + * includes account balances, nonces, codes, and storage. The output is in JSON format. + * + * @param worldState The Ethereum world state to be dumped. + * @param out The PrintWriter to which the state is dumped. + */ public static void dumpWorldState(final MutableWorldState worldState, final PrintWriter out) { out.println("{"); worldState diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java index fe229dc3cf3..820b11442e9 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommandOptionsModule.java @@ -32,6 +32,16 @@ import picocli.CommandLine; import picocli.CommandLine.Option; +/** + * This class, EvmToolCommandOptionsModule, is a Dagger module that provides dependencies for the + * EvmToolCommand. It contains options for setting up the EVM tool, such as whether revert reasons + * should be persisted, the fork to evaluate, the key-value storage to be used, the data path, the + * block number to evaluate against, and the world state update mode. + * + *

The class uses PicoCLI annotations to define these options, which can be provided via the + * command line when running the EVM tool. Each option has a corresponding provider method that + * Dagger uses to inject the option's value where needed. + */ @SuppressWarnings("WeakerAccess") @Module public class EvmToolCommandOptionsModule { @@ -133,4 +143,7 @@ BlockParameter provideBlockParameter() { EvmConfiguration provideEvmConfiguration() { return new EvmConfiguration(jumpDestCacheWeightKilobytes, worldstateUpdateMode); } + + /** Default constructor for the EvmToolCommandOptionsModule class. */ + public EvmToolCommandOptionsModule() {} } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolComponent.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolComponent.java index 176700ee8ee..cc84b59480c 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolComponent.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolComponent.java @@ -26,6 +26,22 @@ import dagger.Component; +/** + * This is a Dagger component interface for the EVM (Ethereum Virtual Machine) tool. It is annotated + * with @Singleton to ensure that only a single instance of this component exists within the Dagger + * component graph. + * + *

The component is composed of several modules that provide the necessary dependencies for the + * EVM tool: - ProtocolModule: Provides the protocol specification. - GenesisFileModule: Provides + * the genesis file for the blockchain. - DataStoreModule: Provides the data store for blockchain + * data. - BlockchainModule: Provides the blockchain instance. - EvmToolCommandOptionsModule: + * Provides the command options for the EVM tool. - MetricsConfigurationModule and + * MetricsSystemModule: Provide the metrics system and its configuration. + * + *

The interface defines methods to get instances of key classes like ProtocolSpec, EVM, + * WorldUpdater, MutableWorldState, and Blockchain. These methods are used by Dagger to inject the + * returned instances where needed. + */ @Singleton @Component( modules = { @@ -39,13 +55,44 @@ }) public interface EvmToolComponent { + /** + * Retrieves the ProtocolSpec instance. ProtocolSpec defines the Ethereum protocol specifications, + * which includes the precompiled contracts, the gas calculator, the EVM, and the private nonce + * calculator. + * + * @return The ProtocolSpec instance. + */ ProtocolSpec getProtocolSpec(); + /** + * Retrieves the EVM instance. EVM (Ethereum Virtual Machine) is responsible for executing the + * bytecode of smart contracts in Ethereum. + * + * @return The EVM instance. + */ EVM getEVM(); + /** + * Retrieves the WorldUpdater instance. WorldUpdater is used to modify the world state, which + * includes the accounts and their associated storage and code. + * + * @return The WorldUpdater instance. + */ WorldUpdater getWorldUpdater(); + /** + * Retrieves the MutableWorldState instance. MutableWorldState represents the world state of + * Ethereum, which includes all accounts, their balances, nonces, codes, and storage. + * + * @return The MutableWorldState instance. + */ MutableWorldState getWorldState(); + /** + * Retrieves the Blockchain instance. Blockchain represents the Ethereum blockchain, which + * includes blocks, transactions, and the world state. + * + * @return The Blockchain instance. + */ Blockchain getBlockchain(); } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java index 506c3911d8f..1283ec039fc 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java @@ -36,11 +36,26 @@ import dagger.Provides; import io.vertx.core.json.JsonObject; +/** + * This class, GenesisFileModule, is a Dagger module that provides dependencies for the GenesisFile. + * It contains options for setting up the GenesisFile, such as the genesis configuration, genesis + * state, block header functions, and the genesis block. + * + *

The class uses Dagger annotations to define these options, which can be provided via the + * command line when running the EVM tool. Each option has a corresponding provider method that + * Dagger uses to inject the option's value where needed. + */ @Module public class GenesisFileModule { private final String genesisConfig; + /** + * Constructs a new GenesisFileModule with the specified genesis configuration. + * + * @param genesisConfig The configuration for the genesis file. This is typically a JSON string + * that specifies various parameters for the genesis block of the blockchain. + */ protected GenesisFileModule(final String genesisConfig) { this.genesisConfig = genesisConfig; } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/JsonUtils.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/JsonUtils.java index 42da8fd950f..7af31054a65 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/JsonUtils.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/JsonUtils.java @@ -24,6 +24,12 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import org.apache.tuweni.bytes.Bytes; +/** + * Utility class for JSON related operations. This class provides a method to create an ObjectMapper + * with standard configurations needed for evmtool. The ObjectMapper is configured to match the + * standard JSON output of Go, and it does not auto close the source. It also registers serializers + * for Address and Bytes classes. This class is not meant to be instantiated. + */ public class JsonUtils { private JsonUtils() {} diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java index 10d31960a82..465f2c48951 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/PrettyPrintSubCommand.java @@ -30,13 +30,31 @@ import org.apache.tuweni.bytes.Bytes; import picocli.CommandLine; +/** + * This class, PrettyPrintSubCommand, is a command-line interface (CLI) command that pretty prints + * EOF (Ethereum Object Format) code. It implements the Runnable interface, meaning it can be used + * in a thread of execution. + * + *

The class is annotated with {@code @CommandLine.Command}, which is a PicoCLI annotation that + * designates this class as a command-line command. The annotation parameters define the command's + * name, description, whether it includes standard help options, and the version provider. + * + *

The command's functionality is defined in the run() method, which is overridden from the + * Runnable interface. + */ @CommandLine.Command( name = COMMAND_NAME, description = "Pretty Prints EOF Code", mixinStandardHelpOptions = true, versionProvider = VersionProvider.class) public class PrettyPrintSubCommand implements Runnable { + /** + * The name of the command for the PrettyPrintSubCommand. This constant is used as the name + * parameter in the @CommandLine.Command annotation. It defines the command name that users should + * enter on the command line to invoke this command. + */ public static final String COMMAND_NAME = "pretty-print"; + @CommandLine.ParentCommand private final EvmToolCommand parentCommand; @CommandLine.Option( @@ -48,10 +66,20 @@ public class PrettyPrintSubCommand implements Runnable { // picocli does it magically @CommandLine.Parameters private final List codeList = new ArrayList<>(); + /** + * Default constructor for the PrettyPrintSubCommand class. This constructor initializes the + * parentCommand to null. + */ public PrettyPrintSubCommand() { this(null); } + /** + * Constructs a new PrettyPrintSubCommand with the specified parent command. + * + * @param parentCommand The parent command for this subcommand. This is typically an instance of + * EvmToolCommand. + */ public PrettyPrintSubCommand(final EvmToolCommand parentCommand) { this.parentCommand = parentCommand; } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/ProtocolModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/ProtocolModule.java index 1b8f557b3b8..a196a35f5ba 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/ProtocolModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/ProtocolModule.java @@ -24,10 +24,25 @@ import dagger.Module; import dagger.Provides; +/** + * This class, ProtocolModule, is a Dagger module that provides dependencies for the ProtocolSpec + * and EVM. It includes the GenesisFileModule, which provides the genesis configuration for the + * blockchain. + * + *

The class uses Dagger annotations to define these dependencies, which can be provided via the + * command line when running the EVM tool. Each dependency has a corresponding provider method that + * Dagger uses to inject the dependency's value where needed. + */ @SuppressWarnings("WeakerAccess") @Module(includes = GenesisFileModule.class) public class ProtocolModule { + /** + * Default constructor for the ProtocolModule class. This constructor doesn't take any arguments + * and doesn't perform any initialization. + */ + public ProtocolModule() {} + @Provides @Singleton ProtocolSpec getProtocolSpec(final ProtocolSchedule protocolSchedule) { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java index 60a09b26e8e..064a093e840 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java @@ -65,12 +65,29 @@ import picocli.CommandLine.Parameters; import picocli.CommandLine.ParentCommand; +/** + * This class, StateTestSubCommand, is a command-line interface (CLI) command that executes an + * Ethereum State Test. It implements the Runnable interface, meaning it can be used in a thread of + * execution. + * + *

The class is annotated with @CommandLine.Command, which is a PicoCLI annotation that + * designates this class as a command-line command. The annotation parameters define the command's + * name, description, whether it includes standard help options, and the version provider. + * + *

The command's functionality is defined in the run() method, which is overridden from the + * Runnable interface. + */ @Command( name = COMMAND_NAME, description = "Execute an Ethereum State Test.", mixinStandardHelpOptions = true, versionProvider = VersionProvider.class) public class StateTestSubCommand implements Runnable { + /** + * The name of the command for the StateTestSubCommand. This constant is used as the name + * parameter in the @CommandLine.Command annotation. It defines the command name that users should + * enter on the command line to invoke this command. + */ public static final String COMMAND_NAME = "state-test"; static final Supplier referenceTestProtocolSchedules = @@ -112,6 +129,10 @@ public class StateTestSubCommand implements Runnable { // picocli does it magically @Parameters private final List stateTestFiles = new ArrayList<>(); + /** + * Default constructor for the StateTestSubCommand class. This constructor doesn't take any + * arguments and initializes the parentCommand to null. PicoCLI requires this constructor. + */ @SuppressWarnings("unused") public StateTestSubCommand() { // PicoCLI requires this diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java index ccabce833a6..d5239848821 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java @@ -51,6 +51,7 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedAccount; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.log.Log; @@ -85,12 +86,45 @@ import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; +/** + * The T8nExecutor class is responsible for executing transactions in the context of the Ethereum + * Virtual Machine (EVM). It extracts transactions from a given input, runs tests on them, and + * generates results including stateRoot, txRoot, receiptsRoot, and logsHash. It also handles block + * rewards and withdrawal processing. This class is part of the EVM tooling within the Hyperledger + * Besu project. + */ public class T8nExecutor { private static final Set

EMPTY_ADDRESS_SET = Set.of(); + /** + * A record that represents a transaction that has been rejected. It contains the index of the + * transaction and the error message explaining why it was rejected. + * + * @param index The index of the rejected transaction. + * @param error The error message explaining why the transaction was rejected. + */ public record RejectedTransaction(int index, String error) {} + /** + * Default constructor for the T8nExecutor class. This constructor does not perform any + * operations. + */ + public T8nExecutor() { + // Default constructor required for Javadoc linting + } + + /** + * Extracts transactions from a given JSON iterator and adds them to the provided transactions + * list. If a transaction cannot be parsed or is invalid, it is added to the rejections list with + * its index and error message. + * + * @param out PrintWriter used for outputting information or errors. + * @param it Iterator over JSON nodes, each representing a transaction. + * @param transactions List of transactions to which parsed transactions are added. + * @param rejections List of RejectedTransaction records to which rejected transactions are added. + * @return The updated list of transactions after parsing and validation. + */ protected static List extractTransactions( final PrintWriter out, final Iterator it, @@ -265,9 +299,11 @@ static T8nResult runTest( .blobGasPricePerGas(calculateExcessBlobGasForParent(protocolSpec, blockHeader)); long blobGasLimit = protocolSpec.getGasLimitCalculator().currentBlobGasLimit(); - protocolSpec - .getBlockHashProcessor() - .processBlockHashes(blockchain, worldState, referenceTestEnv); + if (!referenceTestEnv.isStateTest()) { + protocolSpec + .getBlockHashProcessor() + .processBlockHashes(blockchain, worldState, referenceTestEnv); + } final WorldUpdater rootWorldStateUpdater = worldState.updater(); List receipts = new ArrayList<>(); @@ -318,13 +354,12 @@ static T8nResult runTest( timer.stop(); if (shouldClearEmptyAccounts(fork)) { - final Account coinbase = worldStateUpdater.getOrCreate(blockHeader.getCoinbase()); - if (coinbase != null && coinbase.isEmpty()) { - worldStateUpdater.deleteAccount(coinbase.getAddress()); - } - final Account txSender = worldStateUpdater.getAccount(transaction.getSender()); - if (txSender != null && txSender.isEmpty()) { - worldStateUpdater.deleteAccount(txSender.getAddress()); + var entries = new ArrayList<>(worldState.getAccumulator().getAccountsToUpdate().entrySet()); + for (var entry : entries) { + DiffBasedAccount updated = entry.getValue().getUpdated(); + if (updated != null && updated.isEmpty()) { + worldState.getAccumulator().deleteAccount(entry.getKey()); + } } } if (result.isInvalid()) { @@ -397,7 +432,9 @@ static T8nResult runTest( // block reward // The max production reward was 5 Eth, longs can hold over 18 Eth. - if (!validTransactions.isEmpty() && (rewardString == null || Long.decode(rewardString) > 0)) { + if (!referenceTestEnv.isStateTest() + && !validTransactions.isEmpty() + && (rewardString == null || Long.decode(rewardString) > 0)) { Wei reward = (rewardString == null) ? protocolSpec.getBlockReward() @@ -408,15 +445,24 @@ static T8nResult runTest( } rootWorldStateUpdater.commit(); - // Invoke the withdrawal processor to handle CL withdrawals. - if (!referenceTestEnv.getWithdrawals().isEmpty()) { - try { - protocolSpec - .getWithdrawalsProcessor() - .ifPresent( - p -> p.processWithdrawals(referenceTestEnv.getWithdrawals(), worldState.updater())); - } catch (RuntimeException re) { - resultObject.put("exception", re.getMessage()); + + if (referenceTestEnv.isStateTest()) { + if (!referenceTestEnv.getWithdrawals().isEmpty()) { + resultObject.put("exception", "withdrawals are not supported in state tests"); + } + } else { + // Invoke the withdrawal processor to handle CL withdrawals. + if (!referenceTestEnv.getWithdrawals().isEmpty()) { + try { + protocolSpec + .getWithdrawalsProcessor() + .ifPresent( + p -> + p.processWithdrawals( + referenceTestEnv.getWithdrawals(), worldState.updater())); + } catch (RuntimeException re) { + resultObject.put("exception", re.getMessage()); + } } } @@ -487,7 +533,7 @@ static T8nResult runTest( wr -> { var obj = withdrawlRequests.addObject(); obj.put("sourceAddress", wr.getSourceAddress().toHexString()); - obj.put("validatorPublicKey", wr.getValidatorPublicKey().toHexString()); + obj.put("validatorPubkey", wr.getValidatorPubkey().toHexString()); obj.put("amount", wr.getAmount().toHexString()); }); } @@ -534,12 +580,45 @@ static T8nResult runTest( return new T8nResult(allocObject, bodyBytes, resultObject); } - interface TracerManager { + /** + * The TracerManager interface provides methods for managing OperationTracer instances. It is used + * in the context of Ethereum Virtual Machine (EVM) execution to trace operations. + * + *

The interface defines two methods: - getManagedTracer: This method is used to get a managed + * OperationTracer instance for a specific transaction. - disposeTracer: This method is used to + * dispose of an OperationTracer instance when it is no longer needed. + */ + public interface TracerManager { + + /** + * Retrieves a managed OperationTracer instance for a specific transaction. + * + * @param txIndex The index of the transaction for which the tracer is to be retrieved. + * @param txHash The hash of the transaction for which the tracer is to be retrieved. + * @return The managed OperationTracer instance. + * @throws Exception If an error occurs while retrieving the tracer. + */ OperationTracer getManagedTracer(int txIndex, Hash txHash) throws Exception; + /** + * Disposes of an OperationTracer instance when it is no longer needed. + * + * @param tracer The OperationTracer instance to be disposed. + * @throws IOException If an error occurs while disposing the tracer. + */ void disposeTracer(OperationTracer tracer) throws IOException; } + /** + * A record that represents the result of a transaction test run in the Ethereum Virtual Machine + * (EVM). It contains the final state of the accounts (allocObject), the raw bytes of the + * transactions (bodyBytes), and the result of the test run (resultObject). + * + * @param allocObject The final state of the accounts after the test run. + * @param bodyBytes The raw bytes of the transactions that were run. + * @param resultObject The result of the test run, including stateRoot, txRoot, receiptsRoot, + * logsHash, and other details. + */ @SuppressWarnings("unused") record T8nResult(ObjectNode allocObject, TextNode bodyBytes, ObjectNode resultObject) {} } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java index 1c0a49438e0..ffa6e67b72f 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nServerSubCommand.java @@ -55,6 +55,19 @@ import picocli.CommandLine; import picocli.CommandLine.ParentCommand; +/** + * The T8nServerSubCommand class is responsible for running an Ethereum State Test server. It reads + * the initial state, transactions, and environment from input files or stdin, executes the + * transactions in the Ethereum Virtual Machine (EVM), and writes the final state, transaction + * results, and traces to output files or stdout. + * + *

The class uses the Vert.x library for handling HTTP requests and the picocli library for + * command line argument parsing. It includes options for specifying the host and port to bind to, + * and the base directory for output. + * + *

The class also includes a TracerManager for managing OperationTracer instances, which are used + * to trace EVM operations when the --json flag is specified. + */ @SuppressWarnings("java:S106") // using standard output is the point of this class @CommandLine.Command( name = "t8n-server", @@ -80,6 +93,10 @@ public class T8nServerSubCommand implements Runnable { @ParentCommand private final EvmToolCommand parentCommand; + /** + * Default constructor for the T8nServerSubCommand class. This constructor is required by PicoCLI + * and assigns null to parentCommand. + */ @SuppressWarnings("unused") public T8nServerSubCommand() { // PicoCLI requires this diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java index 95dabb3183f..d6db1a1e8d2 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java @@ -59,6 +59,19 @@ import picocli.CommandLine.Parameters; import picocli.CommandLine.ParentCommand; +/** + * The T8nSubCommand class is responsible for executing an Ethereum State Test. It reads the initial + * state, transactions, and environment from input files or stdin, executes the transactions in the + * Ethereum Virtual Machine (EVM), and writes the final state, transaction results, and traces to + * output files or stdout. + * + *

The class uses the picocli library for command line argument parsing and includes options for + * specifying the input and output files, the fork to run the transition against, the chain ID, and + * the block reward. + * + *

The class also includes a TracerManager for managing OperationTracer instances, which are used + * to trace EVM operations when the --json flag is specified. + */ @Command( name = COMMAND_NAME, aliases = COMMAND_ALIAS, @@ -154,12 +167,22 @@ public void consumeParameters( } } + /** + * Default constructor for the T8nSubCommand class. This constructor is required by PicoCLI and + * assigns parent command to 'null'. + */ @SuppressWarnings("unused") public T8nSubCommand() { // PicoCLI requires this parentCommand = null; } + /** + * Constructor for the T8nSubCommand class with a parent command. This constructor is required by + * PicoCLI. + * + * @param parentCommand The parent command for this sub command. + */ @SuppressWarnings("unused") public T8nSubCommand(final EvmToolCommand parentCommand) { // PicoCLI requires this too diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/VersionProvider.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/VersionProvider.java index d97fded8825..6f3ff330717 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/VersionProvider.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/VersionProvider.java @@ -18,8 +18,26 @@ import picocli.CommandLine; +/** + * The VersionProvider class is responsible for providing the version of the Hyperledger Besu EVM + * tool. It implements the IVersionProvider interface from the picocli library. + * + *

The getVersion method returns a string array containing the version of the Hyperledger Besu + * EVM tool. + */ public class VersionProvider implements CommandLine.IVersionProvider { + /** + * Default constructor for the VersionProvider class. This constructor does not perform any + * operations. + */ + public VersionProvider() {} + + /** + * This method returns the version of the Hyperledger Besu EVM tool. + * + * @return A string array containing the version of the Hyperledger Besu EVM tool. + */ @Override public String[] getVersion() { return new String[] {"Hyperledger Besu evm " + BesuInfo.shortVersion()}; diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/prague-deposit.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/prague-deposit.json index 80e415b3fc2..d48fc66c704 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/prague-deposit.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/prague-deposit.json @@ -63,6 +63,11 @@ "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", "storage": {} }, + "0x0aae40965e6800cd9b1f4b05ff21581047e3f91e": { + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500", + "balance": "0x0", + "nonce": "0x1" + }, "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "nonce": "0x00", "balance": "0xad78ebc5ac62000000", @@ -180,11 +185,13 @@ "balance": "0x0", "nonce": "0x1" }, - "0x25a219378dad9b3503c8268c9ca836a52427a4fb": { + "0x0aae40965e6800cd9b1f4b05ff21581047e3f91e": { + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500", "storage": { "0x0000000000000000000000000000000000000000000000000000000000000000": "0xe4fb5d47f70d54b4f36777ea4c882cf767f93d8f8170285d97a1b8275dfe4dbb" }, - "balance": "0x0" + "balance": "0x0", + "nonce": "0x1" }, "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "balance": "0xaa00be18c288efd690", @@ -193,7 +200,7 @@ }, "body": "0xf90404f901ff8007830f42409400000000219ab540356cbb839cbe05303d7705fa8901bc16d674ec800000b9019422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000d0000000000000000000000000000000000000000000000000000000000000011085acb6376c2707b118225da41825974c12b5924a05c6a53b988c9cbc33c55b05000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000325a0ffeb1d6e7ef8e9ee9b64dcdc3b056f9a1d2b94c1572f1949954e712364604575a03d0f42bad795205de84db8d4ab10b9abd0d081ffe560cbf45f6c281768112a69f901ff0107830f42409400000000219ab540356cbb839cbe05303d7705fa8901bc16d674ec800000b9019422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000d0000000000000000000000000000000000000000000000000000000000000011085acb6376c2707b118225da41825974c12b5924a05c6a53b988c9cbc33c55b05000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000326a05bb08e348c9c4b0a2e15d04f4a89d1a210d013205de8d3d93e38e5c1b0c8d8aba04300c0f575d9908d2cbc3413ab82895678bb8f3ef356224dd1e7cb972f2c4855", "result": { - "stateRoot": "0x0dd3e32e0081ff7ca5a0236b257676f277999ec2b8da5b31e19400715de907f1", + "stateRoot": "0x3aa7839837ee1564276a0a05554e35215353a97c4e255c3aacbcd7c7819daefa", "txRoot": "0x2b790bf82ef7259a0e4513d1b89a77d81e99672ba68758ef2ba3fde32851d023", "receiptsRoot": "0x9c8d7a917ecb3ff2566f264abbf39131e51b08b07eb2b69cb46989d79d985593", "logsHash": "0x43e31613bfefc1f55d8b3ca2b61f933f3838d523dc11cb5d7ffdd2ecf0ab5d49", diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/prague-withdrawal.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/prague-withdrawal.json index 85ad0d7e9ab..66dcf3e85e3 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/prague-withdrawal.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/prague-withdrawal.json @@ -64,6 +64,11 @@ "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", "storage": {} }, + "0x0aae40965e6800cd9b1f4b05ff21581047e3f91e": { + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500", + "balance": "0x0", + "nonce": "0x1" + }, "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "nonce": "0x00", "balance": "0xad78ebc5ac62000000", @@ -209,11 +214,13 @@ "balance": "0x10", "nonce": "0x1" }, - "0x25a219378dad9b3503c8268c9ca836a52427a4fb": { + "0x0aae40965e6800cd9b1f4b05ff21581047e3f91e": { + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500", "storage": { "0x0000000000000000000000000000000000000000000000000000000000000000": "0x10715cfbefdb8a0cb2f7d7ca5ee6d1ea65515ecb41cff0a22d1e11716a9d27fb" }, - "balance": "0x0" + "balance": "0x0", + "nonce": "0x1" }, "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "balance": "0xad78ebc5ac619bbcea", @@ -222,7 +229,7 @@ }, "body": "0xf903e5f903e28007830f424094000000000000000000000000000000000000020080b90380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001fffffffffffffffe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fffffffffffffffe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fffffffffffffffe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007fffffffffffffffe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009fffffffffffffffe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bfffffffffffffffe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dfffffffffffffffe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000026a0963cb6620fe5828cbc93bb7139d3f4501067d76275dff648bf48c3c100ca8dd4a04ac396104a5be4643406718f59a6e74d62a32777f5f6135e55e805e87612013c", "result": { - "stateRoot": "0xf682e4f8f820f44fb43a20c3db274bc3c9fcb9e52cc1af3c3ea7cb21fdb2250b", + "stateRoot": "0xdba3aee0733886dac565d2a654d8fac1953fd913261f44b3aa31f153e159e98a", "txRoot": "0x8521df63211790726b6f1a437bb0fd4b27c00e13e7678d324c4cfddb8d834ad2", "receiptsRoot": "0x4bd8bd5580caf4ed45f873794ad7ff9d6fd2363ae529269b17b891b68d349d75", "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", @@ -252,82 +259,82 @@ "withdrawalRequests": [ { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "amount": "0xfffffffffffffffe" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002", "amount": "0x0000000000000000" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003", "amount": "0xfffffffffffffffe" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004", "amount": "0x0000000000000000" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005", "amount": "0xfffffffffffffffe" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006", "amount": "0x0000000000000000" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007", "amount": "0xfffffffffffffffe" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008", "amount": "0x0000000000000000" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009", "amount": "0xfffffffffffffffe" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a", + "validatorPubkey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a", "amount": "0x0000000000000000" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b", + "validatorPubkey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b", "amount": "0xfffffffffffffffe" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c", + "validatorPubkey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c", "amount": "0x0000000000000000" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d", + "validatorPubkey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d", "amount": "0xfffffffffffffffe" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e", + "validatorPubkey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e", "amount": "0x0000000000000000" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f", + "validatorPubkey": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f", "amount": "0xfffffffffffffffe" }, { "sourceAddress": "0x0000000000000000000000000000000000000200", - "validatorPublicKey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", + "validatorPubkey": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", "amount": "0x0000000000000000" } ] diff --git a/ethereum/mock-p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/testing/MockNetwork.java b/ethereum/mock-p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/testing/MockNetwork.java index b443afe9f94..d7a3c2b5065 100644 --- a/ethereum/mock-p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/testing/MockNetwork.java +++ b/ethereum/mock-p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/testing/MockNetwork.java @@ -55,6 +55,11 @@ public final class MockNetwork { private final Map nodes = new HashMap<>(); private final List capabilities; + /** + * Constructs a new MockNetwork with the specified capabilities. + * + * @param capabilities a list of capabilities that the mock network should have. + */ public MockNetwork(final List capabilities) { this.capabilities = capabilities; } diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java index 378637f3634..1048f6238a2 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java @@ -249,7 +249,8 @@ public CandidateBlock( @JsonProperty("uncleHeaders") final Object uncleHeaders, @JsonProperty("withdrawals") final Object withdrawals, @JsonProperty("depositRequests") final Object depositRequests, - @JsonProperty("withdrawalRequests") final Object withdrawalRequests) { + @JsonProperty("withdrawalRequests") final Object withdrawalRequests, + @JsonProperty("consolidationRequests") final Object consolidationRequests) { boolean blockVaid = true; // The BLOCK__WrongCharAtRLP_0 test has an invalid character in its rlp string. Bytes rlpAttempt = null; diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java index 3cd248e3aef..636e23f8786 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java @@ -83,6 +83,8 @@ Withdrawal asWithdrawal() { private final Bytes32 beaconRoot; + private final boolean isStateTest; + /** * Public constructor. * @@ -120,7 +122,8 @@ public ReferenceTestEnv( @JsonProperty("parentGasLimit") final String parentGasLimit, @JsonProperty("parentGasUsed") final String parentGasUsed, @JsonProperty("parentTimestamp") final String parentTimestamp, - @JsonProperty("parentUncleHash") final String _parentUncleHash) { + @JsonProperty("parentUncleHash") final String _parentUncleHash, + @JsonProperty("isStateTest") final String isStateTest) { super( generateTestBlockHash(previousHash, number), Hash.EMPTY_LIST_HASH, // ommersHash @@ -164,10 +167,16 @@ public ReferenceTestEnv( Map.entry( Long.decode(entry.getKey()), Hash.fromHexString(entry.getValue()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - this.beaconRoot = - beaconRoot == null - ? (currentBeaconRoot == null ? null : Hash.fromHexString(currentBeaconRoot)) - : Hash.fromHexString(beaconRoot); + if (beaconRoot == null) { + if (currentBeaconRoot == null) { + this.beaconRoot = null; + } else { + this.beaconRoot = Hash.fromHexString(currentBeaconRoot); + } + } else { + this.beaconRoot = Hash.fromHexString(beaconRoot); + } + this.isStateTest = Boolean.parseBoolean(isStateTest); } @Override @@ -239,6 +248,10 @@ public Map getBlockHashes() { return blockHashes; } + public boolean isStateTest() { + return isStateTest; + } + @Override public boolean equals(final Object o) { if (this == o) return true; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java index f571ff2ff03..9f065aee98b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java @@ -128,7 +128,6 @@ public A getWrappedAccount() { public void setWrappedAccount(final A account) { if (this.account == null) { this.account = account; - storageWasCleared = false; } else { throw new IllegalStateException("Already tracking a wrapped account"); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java index 9b57b6ab9e3..4cbda732798 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.Optional; +import org.apache.tuweni.bytes.Bytes; + /** * An object that buffers updates made over a particular {@link WorldView}. * @@ -73,8 +75,29 @@ default MutableAccount createAccount(final Address address) { * #createAccount(Address)} (and thus all his fields will be zero/empty). */ default MutableAccount getOrCreate(final Address address) { - final MutableAccount account = getAccount(address); - return account == null ? createAccount(address) : account; + MutableAccount account = getAccount(address); + if (account == null) { + account = createAccount(address); + if (parentUpdater().isPresent() && parentUpdater().get().isDeleted(address)) { + account.clearStorage(); + account.setCode(Bytes.EMPTY); + } + } + return account; + } + + /** + * Check this and parent updaters to see if an address has been deleted since the last persist + * + * @param address address to check + * @return true if any updaters have marked the address as deleted. + */ + default boolean isDeleted(final Address address) { + if (getDeletedAccountAddresses().contains(address)) { + return true; + } else { + return parentUpdater().map(wu -> wu.isDeleted(address)).orElse(false); + } } /** diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index fd7f3291b3b..dbbb5e4f2bf 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -70,7 +70,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'p8jZoKgvi9o8JpVLXTlwh9HoIot4A62hKFHwSOaTF+k=' + knownHash = 'Q6EK5By3BNKNa/JYqYjFw43VXWL0KVBUV3dGEQBjZ70=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/WithdrawalRequest.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/WithdrawalRequest.java index eeadd80418d..f07d0d08d03 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/WithdrawalRequest.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/WithdrawalRequest.java @@ -38,7 +38,7 @@ public interface WithdrawalRequest { * * @return public key of validator */ - PublicKey getValidatorPublicKey(); + PublicKey getValidatorPubkey(); /** * The amount for withdrawal