From 1b7b6e8025eb9160c073ff9fc1854aa03751053b Mon Sep 17 00:00:00 2001 From: Bhanu Pulluri <59369753+pullurib@users.noreply.github.com> Date: Wed, 4 Dec 2024 11:53:40 -0500 Subject: [PATCH 01/10] Add RPC WS options to specify password file for keystore and truststore (#7970) * Add RPC WS options to specify password file for keystore and truststore Signed-off-by: Bhanu Pulluri * update changelog Signed-off-by: Bhanu Pulluri --------- Signed-off-by: Bhanu Pulluri Signed-off-by: Bhanu Pulluri <59369753+pullurib@users.noreply.github.com> Co-authored-by: Bhanu Pulluri Co-authored-by: Fabio Di Fabio --- CHANGELOG.md | 2 + .../besu/cli/options/RpcWebsocketOptions.java | 66 ++++++++++++++----- .../src/test/resources/everything_config.toml | 2 + .../websocket/WebSocketConfiguration.java | 52 +++++++++++++-- .../jsonrpc/websocket/WebSocketService.java | 18 ++++- .../websocket/WebSocketServiceTLSTest.java | 13 +++- 6 files changed, 129 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0444a9deb..4ee0947a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,9 +43,11 @@ - Add a method to check if a metric category is enabled to the plugin API [#7832](https://github.com/hyperledger/besu/pull/7832) - Add a new metric collector for counters which get their value from suppliers [#7894](https://github.com/hyperledger/besu/pull/7894) - Add account and state overrides to `eth_call` [#7801](https://github.com/hyperledger/besu/pull/7801) and `eth_estimateGas` [#7890](https://github.com/hyperledger/besu/pull/7890) +- Add RPC WS options to specify password file for keystore and truststore [#7970](https://github.com/hyperledger/besu/pull/7970) - Prometheus Java Metrics library upgraded to version 1.3.3 [#7880](https://github.com/hyperledger/besu/pull/7880) - Add histogram to Prometheus metrics system [#7944](https://github.com/hyperledger/besu/pull/7944) + ### Bug fixes - Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825) - Fix CVE-2024-47535 [7878](https://github.com/hyperledger/besu/pull/7878) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/RpcWebsocketOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/RpcWebsocketOptions.java index 2dde517cc..fb7c73e7e 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/RpcWebsocketOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/RpcWebsocketOptions.java @@ -37,6 +37,35 @@ import picocli.CommandLine; /** This class represents the WebSocket options for the RPC. */ public class RpcWebsocketOptions { + + static class KeystorePasswordOptions { + @CommandLine.Option( + names = {"--rpc-ws-ssl-keystore-password"}, + paramLabel = "", + description = "Password for the WebSocket RPC keystore file") + private String rpcWsKeyStorePassword; + + @CommandLine.Option( + names = {"--rpc-ws-ssl-keystore-password-file"}, + paramLabel = "", + description = "File containing the password for WebSocket keystore.") + private String rpcWsKeystorePasswordFile; + } + + static class TruststorePasswordOptions { + @CommandLine.Option( + names = {"--rpc-ws-ssl-truststore-password"}, + paramLabel = "", + description = "Password for the WebSocket RPC truststore file") + private String rpcWsTrustStorePassword; + + @CommandLine.Option( + names = {"--rpc-ws-ssl-truststore-password-file"}, + paramLabel = "", + description = "File containing the password for WebSocket truststore.") + private String rpcWsTruststorePasswordFile; + } + @CommandLine.Option( names = {"--rpc-ws-authentication-jwt-algorithm"}, description = @@ -131,12 +160,6 @@ public class RpcWebsocketOptions { description = "Path to the keystore file for the WebSocket RPC service") private String rpcWsKeyStoreFile = null; - @CommandLine.Option( - names = {"--rpc-ws-ssl-keystore-password"}, - paramLabel = "", - description = "Password for the WebSocket RPC keystore file") - private String rpcWsKeyStorePassword = null; - @CommandLine.Option( names = {"--rpc-ws-ssl-key-file"}, paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP, @@ -167,12 +190,6 @@ public class RpcWebsocketOptions { description = "Path to the truststore file for the WebSocket RPC service") private String rpcWsTrustStoreFile = null; - @CommandLine.Option( - names = {"--rpc-ws-ssl-truststore-password"}, - paramLabel = "", - description = "Password for the WebSocket RPC truststore file") - private String rpcWsTrustStorePassword = null; - @CommandLine.Option( names = {"--rpc-ws-ssl-trustcert-file"}, paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP, @@ -185,6 +202,12 @@ public class RpcWebsocketOptions { description = "Type of the truststore (JKS, PKCS12, PEM)") private String rpcWsTrustStoreType = null; + @CommandLine.ArgGroup(exclusive = true, multiplicity = "1") + private KeystorePasswordOptions keystorePasswordOptions; + + @CommandLine.ArgGroup(exclusive = true, multiplicity = "1") + private TruststorePasswordOptions truststorePasswordOptions; + /** Default Constructor. */ public RpcWebsocketOptions() {} @@ -292,7 +315,7 @@ public class RpcWebsocketOptions { commandLine, "--rpc-ws-ssl-keystore-file", rpcWsKeyStoreFile == null, - List.of("--rpc-ws-ssl-keystore-password")); + List.of("--rpc-ws-ssl-keystore-password", "--rpc-ws-ssl-keystore-password-file")); } } @@ -302,7 +325,7 @@ public class RpcWebsocketOptions { commandLine, "--rpc-ws-ssl-truststore-file", rpcWsTrustStoreFile == null, - List.of("--rpc-ws-ssl-truststore-password")); + List.of("--rpc-ws-ssl-truststore-password", "--rpc-ws-ssl-truststore-password-file")); } if (isRpcWsAuthenticationEnabled) { @@ -343,16 +366,27 @@ public class RpcWebsocketOptions { webSocketConfiguration.setTimeoutSec(wsTimoutSec); webSocketConfiguration.setSslEnabled(isRpcWsSslEnabled); webSocketConfiguration.setKeyStorePath(rpcWsKeyStoreFile); - webSocketConfiguration.setKeyStorePassword(rpcWsKeyStorePassword); webSocketConfiguration.setKeyStoreType(rpcWsKeyStoreType); webSocketConfiguration.setClientAuthEnabled(isRpcWsClientAuthEnabled); webSocketConfiguration.setTrustStorePath(rpcWsTrustStoreFile); - webSocketConfiguration.setTrustStorePassword(rpcWsTrustStorePassword); webSocketConfiguration.setTrustStoreType(rpcWsTrustStoreType); webSocketConfiguration.setKeyPath(rpcWsKeyFile); webSocketConfiguration.setCertPath(rpcWsCertFile); webSocketConfiguration.setTrustCertPath(rpcWsTrustCertFile); + if (keystorePasswordOptions != null) { + webSocketConfiguration.setKeyStorePassword(keystorePasswordOptions.rpcWsKeyStorePassword); + webSocketConfiguration.setKeyStorePasswordFile( + keystorePasswordOptions.rpcWsKeystorePasswordFile); + } + + if (truststorePasswordOptions != null) { + webSocketConfiguration.setTrustStorePassword( + truststorePasswordOptions.rpcWsTrustStorePassword); + webSocketConfiguration.setTrustStorePasswordFile( + truststorePasswordOptions.rpcWsTruststorePasswordFile); + } + return webSocketConfiguration; } diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index e210d9fa9..3904fe90d 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -123,10 +123,12 @@ rpc-ws-authentication-jwt-public-key-file="none" rpc-ws-ssl-enabled=false rpc-ws-ssl-keystore-file="none.pfx" rpc-ws-ssl-keystore-password="none.passwd" +rpc-ws-ssl-keystore-password-file="none.txt" rpc-ws-ssl-keystore-type="none" rpc-ws-ssl-client-auth-enabled=false rpc-ws-ssl-truststore-file="none.pfx" rpc-ws-ssl-truststore-password="none.passwd" +rpc-ws-ssl-truststore-password-file="none.txt" rpc-ws-ssl-truststore-type="none" rpc-ws-ssl-key-file="none.pfx" rpc-ws-ssl-cert-file="none.pfx" diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketConfiguration.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketConfiguration.java index 42905b96a..278e06bd7 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketConfiguration.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketConfiguration.java @@ -20,6 +20,10 @@ import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions; import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.JwtAlgorithm; import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -54,11 +58,13 @@ public class WebSocketConfiguration { private Optional keyStorePath = Optional.empty(); private Optional keyStorePassword = Optional.empty(); private Optional keyStoreType = Optional.of("JKS"); // Default to JKS + private Optional keyStorePasswordFile = Optional.empty(); private boolean clientAuthEnabled = false; private Optional trustStorePath = Optional.empty(); private Optional trustStorePassword = Optional.empty(); private Optional trustStoreType = Optional.of("JKS"); // Default to JKS + private Optional trustStorePasswordFile = Optional.empty(); // For PEM format private Optional keyPath = Optional.empty(); @@ -191,8 +197,11 @@ public class WebSocketConfiguration { this.keyStorePath = Optional.ofNullable(keyStorePath); } - public Optional getKeyStorePassword() { - return keyStorePassword; + public Optional getKeyStorePassword() throws IOException { + if (keyStorePassword.isPresent()) { + return keyStorePassword; + } + return Optional.ofNullable(getKeystorePasswordFromFile()); } public void setKeyStorePassword(final String keyStorePassword) { @@ -245,8 +254,11 @@ public class WebSocketConfiguration { } // Truststore Password - public Optional getTrustStorePassword() { - return trustStorePassword; + public Optional getTrustStorePassword() throws IOException { + if (trustStorePassword.isPresent()) { + return trustStorePassword; + } + return Optional.ofNullable(getTruststorePasswordFromFile()); } public void setTrustStorePassword(final String trustStorePassword) { @@ -258,6 +270,38 @@ public class WebSocketConfiguration { return trustStoreType; } + public void setKeyStorePasswordFile(final String keyStorePasswordFile) { + this.keyStorePasswordFile = Optional.ofNullable(keyStorePasswordFile); + } + + public void setTrustStorePasswordFile(final String trustStorePasswordFile) { + this.trustStorePasswordFile = Optional.ofNullable(trustStorePasswordFile); + } + + private String loadPasswordFromFile(final String passwordFile) throws IOException { + if (passwordFile != null) { + Path path = Path.of(passwordFile); + if (Files.exists(path)) { + return Files.readString(path, StandardCharsets.UTF_8).trim(); + } + } + return null; + } + + public String getKeystorePasswordFromFile() throws IOException { + if (keyStorePasswordFile.isPresent()) { + return loadPasswordFromFile(keyStorePasswordFile.get()); + } + return null; + } + + public String getTruststorePasswordFromFile() throws IOException { + if (trustStorePasswordFile.isPresent()) { + return loadPasswordFromFile(trustStorePasswordFile.get()); + } + return null; + } + public void setTrustStoreType(final String trustStoreType) { this.trustStoreType = Optional.ofNullable(trustStoreType); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketService.java index 31c86b18d..d72a659cd 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketService.java @@ -122,9 +122,16 @@ public class WebSocketService { // Check if SSL/TLS is enabled in the configuration if (configuration.isSslEnabled()) { serverOptions.setSsl(true); + String keystorePassword = null; String keystorePath = configuration.getKeyStorePath().orElse(null); - String keystorePassword = configuration.getKeyStorePassword().orElse(null); + try { + keystorePassword = configuration.getKeyStorePassword().orElse(null); + } catch (Exception e) { + LOG.error("Error reading keystore password", e); + resultFuture.completeExceptionally(e); + return resultFuture; + } String keyPath = configuration.getKeyPath().orElse(null); String certPath = configuration.getCertPath().orElse(null); @@ -146,9 +153,16 @@ public class WebSocketService { // Set up truststore for client authentication (mTLS) if (configuration.isClientAuthEnabled()) { serverOptions.setClientAuth(ClientAuth.REQUIRED); + String truststorePassword; String truststorePath = configuration.getTrustStorePath().orElse(null); - String truststorePassword = configuration.getTrustStorePassword().orElse(""); + try { + truststorePassword = configuration.getTrustStorePassword().orElse(null); + } catch (Exception e) { + LOG.error("Error reading truststore password", e); + resultFuture.completeExceptionally(e); + return resultFuture; + } String truststoreType = configuration.getTrustStoreType().orElse("JKS"); String trustCertPath = configuration.getTrustCertPath().orElse(null); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTLSTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTLSTest.java index 9a227dc43..da4fd9458 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTLSTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTLSTest.java @@ -30,6 +30,9 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.io.File; import java.io.FileOutputStream; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.file.Files; import java.security.KeyStore; import java.util.Base64; import java.util.HashMap; @@ -390,14 +393,20 @@ public class WebSocketServiceTLSTest { clientTrustStore.store(fos, "password".toCharArray()); } + File tempFile = File.createTempFile("pwdfile", ".txt"); + tempFile.deleteOnExit(); + try (Writer writer = Files.newBufferedWriter(tempFile.toPath(), Charset.defaultCharset())) { + writer.write("password"); + } + // Configure WebSocket with SSL and client authentication enabled config.setSslEnabled(true); config.setKeyStorePath(serverKeystoreFile.getAbsolutePath()); - config.setKeyStorePassword("password"); + config.setKeyStorePasswordFile(tempFile.getAbsolutePath()); config.setKeyStoreType("PKCS12"); config.setClientAuthEnabled(true); config.setTrustStorePath(serverTruststoreFile.getAbsolutePath()); - config.setTrustStorePassword("password"); + config.setTrustStorePasswordFile(tempFile.getAbsolutePath()); config.setTrustStoreType("PKCS12"); // Create and start WebSocketService From 472357f118832ae4a0374a31bea71d33d2408259 Mon Sep 17 00:00:00 2001 From: Simon Dudley Date: Thu, 5 Dec 2024 11:50:23 +1000 Subject: [PATCH 02/10] EIP-7742 - gas calculation using target_blobs_per_block (#7823) Signed-off-by: Simon Dudley --- .../processor/TransactionTracerTest.java | 4 - .../BlobSizeTransactionSelectorTest.java | 12 +-- .../mainnet/AbstractBlockProcessor.java | 4 +- .../feemarket/ExcessBlobGasCalculator.java | 3 +- .../BlobGasValidationRule.java | 9 +-- .../MainnetTransactionValidatorTest.java | 4 +- .../BlobGasValidationRuleTest.java | 74 ++++++++++++++++--- .../gascalculator/CancunGasCalculator.java | 46 +++--------- .../besu/evm/gascalculator/GasCalculator.java | 48 ++++++++---- .../CancunGasCalculatorTest.java | 14 +++- .../PragueGasCalculatorTest.java | 54 +++++++++++++- 11 files changed, 185 insertions(+), 87 deletions(-) diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 6626a66bb..3f8be1815 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java @@ -16,10 +16,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -124,7 +121,6 @@ public class TransactionTracerTest { when(blockchain.getChainHeadHeader()).thenReturn(blockHeader); when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator); when(protocolContext.getBadBlockManager()).thenReturn(badBlockManager); - lenient().when(gasCalculator.computeExcessBlobGas(anyLong(), anyInt())).thenReturn(0L); } @Test diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java index 123faf2d1..7d9292935 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java @@ -17,7 +17,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.Answers.RETURNS_DEEP_STUBS; -import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @@ -61,7 +61,7 @@ class BlobSizeTransactionSelectorTest { Suppliers.memoize(SignatureAlgorithmFactory::getInstance); private static final KeyPair KEYS = SIGNATURE_ALGORITHM.get().generateKeyPair(); - private static final long BLOB_GAS_PER_BLOB = CancunGasCalculator.BLOB_GAS_PER_BLOB; + private static final long BLOB_GAS_PER_BLOB = new CancunGasCalculator().getBlobGasPerBlob(); private static final int MAX_BLOBS = 6; private static final long MAX_BLOB_GAS = BLOB_GAS_PER_BLOB * MAX_BLOBS; @@ -89,8 +89,8 @@ class BlobSizeTransactionSelectorTest { @Test void firstBlobTransactionIsSelected() { when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS); - when(blockSelectionContext.gasCalculator().blobGasCost(anyInt())) - .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Integer.class)); + when(blockSelectionContext.gasCalculator().blobGasCost(anyLong())) + .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class)); final var selector = new BlobSizeTransactionSelector(blockSelectionContext); @@ -131,8 +131,8 @@ class BlobSizeTransactionSelectorTest { @Test void returnsTooLargeForRemainingBlobGas() { when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS); - when(blockSelectionContext.gasCalculator().blobGasCost(anyInt())) - .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Integer.class)); + when(blockSelectionContext.gasCalculator().blobGasCost(anyLong())) + .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class)); final var selector = new BlobSizeTransactionSelector(blockSelectionContext); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index b8863fe14..496652cdf 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -37,7 +37,6 @@ import org.hyperledger.besu.ethereum.trie.MerkleTrieException; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; -import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldState; @@ -174,7 +173,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { currentGasUsed += transaction.getGasLimit() - transactionProcessingResult.getGasRemaining(); if (transaction.getVersionedHashes().isPresent()) { currentBlobGasUsed += - (transaction.getVersionedHashes().get().size() * CancunGasCalculator.BLOB_GAS_PER_BLOB); + (transaction.getVersionedHashes().get().size() + * protocolSpec.getGasCalculator().getBlobGasPerBlob()); } final TransactionReceipt transactionReceipt = diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java index f6372097b..b483e4292 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java @@ -36,7 +36,8 @@ public class ExcessBlobGasCalculator { .getGasCalculator() .computeExcessBlobGas( parentHeader.getExcessBlobGas().map(BlobGas::toLong).orElse(0L), - parentHeader.getBlobGasUsed().orElse(0L)); + parentHeader.getBlobGasUsed().orElse(0L), + parentHeader.getTargetBlobsPerBlock()); return BlobGas.of(headerExcess); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java index 3a34d7be3..2f24d6183 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java @@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.mainnet.headervalidationrules; import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.mainnet.DetachedBlockHeaderValidationRule; -import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.slf4j.Logger; @@ -45,7 +44,8 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L); long calculatedExcessBlobGas = - gasCalculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed); + gasCalculator.computeExcessBlobGas( + parentExcessBlobGas, parentBlobGasUsed, parent.getTargetBlobsPerBlock()); if (headerExcessBlobGas != calculatedExcessBlobGas) { LOG.info( @@ -55,10 +55,9 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule return false; } long headerBlobGasUsed = header.getBlobGasUsed().orElse(0L); - if (headerBlobGasUsed % CancunGasCalculator.BLOB_GAS_PER_BLOB != 0) { + if (headerBlobGasUsed % gasCalculator.getBlobGasPerBlob() != 0) { LOG.info( - "blob gas used must be multiple of GAS_PER_BLOB ({})", - CancunGasCalculator.BLOB_GAS_PER_BLOB); + "blob gas used must be multiple of GAS_PER_BLOB ({})", gasCalculator.getBlobGasPerBlob()); return false; } return true; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index 182f1b609..c673bf08f 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -24,7 +24,7 @@ import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -589,7 +589,7 @@ public class MainnetTransactionValidatorTest { @Test public void shouldAcceptTransactionWithAtLeastOneBlob() { - when(gasCalculator.blobGasCost(anyInt())).thenReturn(2L); + when(gasCalculator.blobGasCost(anyLong())).thenReturn(2L); final TransactionValidator validator = createTransactionValidator( gasCalculator, diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java index 858a8a9a2..d14089e0b 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java @@ -20,26 +20,37 @@ import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; +import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; +import org.apache.tuweni.units.bigints.UInt64; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** Tests for the {@link BlobGasValidationRule} class. */ public class BlobGasValidationRuleTest { - private CancunGasCalculator gasCalculator; - private BlobGasValidationRule blobGasValidationRule; + private CancunGasCalculator cancunGasCalculator; + private BlobGasValidationRule cancunBlobGasValidationRule; + + private PragueGasCalculator pragueGasCalculator; + private BlobGasValidationRule pragueBlobGasValidationRule; @BeforeEach public void setUp() { - gasCalculator = new CancunGasCalculator(); - blobGasValidationRule = new BlobGasValidationRule(gasCalculator); + cancunGasCalculator = new CancunGasCalculator(); + cancunBlobGasValidationRule = new BlobGasValidationRule(cancunGasCalculator); + + pragueGasCalculator = new PragueGasCalculator(); + pragueBlobGasValidationRule = new BlobGasValidationRule(pragueGasCalculator); } - /** Tests that the header blob gas matches the calculated blob gas and passes validation. */ + /** + * Cancun EIP-4844 - Tests that the header blob gas matches the calculated blob gas and passes + * validation. + */ @Test public void validateHeader_BlobGasMatchesCalculated_SuccessValidation() { - long target = gasCalculator.getTargetBlobGasPerBlock(); + long target = cancunGasCalculator.getTargetBlobGasPerBlock(); // Create parent header final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); @@ -52,15 +63,16 @@ public class BlobGasValidationRuleTest { headerBuilder.excessBlobGas(BlobGas.of(1L)); final BlockHeader header = headerBuilder.buildHeader(); - assertThat(blobGasValidationRule.validate(header, parentHeader)).isTrue(); + assertThat(cancunBlobGasValidationRule.validate(header, parentHeader)).isTrue(); } /** - * Tests that the header blob gas is different from the calculated blob gas and fails validation. + * Cancun EIP-4844 - Tests that the header blob gas is different from the calculated blob gas and + * fails validation. */ @Test public void validateHeader_BlobGasDifferentFromCalculated_FailsValidation() { - long target = gasCalculator.getTargetBlobGasPerBlock(); + long target = cancunGasCalculator.getTargetBlobGasPerBlock(); // Create parent header final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); @@ -72,6 +84,48 @@ public class BlobGasValidationRuleTest { final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); final BlockHeader header = headerBuilder.buildHeader(); - assertThat(blobGasValidationRule.validate(header, parentHeader)).isFalse(); + assertThat(cancunBlobGasValidationRule.validate(header, parentHeader)).isFalse(); + } + + /** + * Prague EIP-7742 - Tests that the header blob gas matches the calculated blob gas and passes + * validation. + */ + @Test + public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Target3() { + // Create parent header + final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); + parentBuilder.excessBlobGas(BlobGas.of(1L)); + parentBuilder.blobGasUsed(pragueGasCalculator.blobGasCost(3)); + parentBuilder.targetBlobsPerBlock(UInt64.valueOf(3)); + final BlockHeader parentHeader = parentBuilder.buildHeader(); + + // Create block header with matching excessBlobGas + final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); + headerBuilder.excessBlobGas(BlobGas.of(1L)); + final BlockHeader header = headerBuilder.buildHeader(); + + assertThat(pragueBlobGasValidationRule.validate(header, parentHeader)).isTrue(); + } + + /** + * Prague EIP-7742 - Tests that the header blob gas matches the calculated blob gas and passes + * validation. + */ + @Test + public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Target4() { + // Create parent header + final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); + parentBuilder.excessBlobGas(BlobGas.of(1L)); + parentBuilder.blobGasUsed(pragueGasCalculator.blobGasCost(4)); + parentBuilder.targetBlobsPerBlock(UInt64.valueOf(4)); + final BlockHeader parentHeader = parentBuilder.buildHeader(); + + // Create block header with matching excessBlobGas + final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); + headerBuilder.excessBlobGas(BlobGas.of(1L)); + final BlockHeader header = headerBuilder.buildHeader(); + + assertThat(pragueBlobGasValidationRule.validate(header, parentHeader)).isTrue(); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java index 79165e5c2..2d6168322 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java @@ -44,13 +44,13 @@ public class CancunGasCalculator extends ShanghaiGasCalculator { private static final long TSTORE_GAS = WARM_STORAGE_READ_COST; /** - * The blob gas cost per blob. This is the gas cost for each blob of blob that is added to the + * The blob gas cost per blob. This is the gas cost for each blob of data that is added to the * block. */ - public static final long BLOB_GAS_PER_BLOB = 1 << 17; + private static final long BLOB_GAS_PER_BLOB = 1 << 17; /** The target blob gas per block. */ - public static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000; + static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000; // EIP-1153 @Override @@ -64,8 +64,13 @@ public class CancunGasCalculator extends ShanghaiGasCalculator { } @Override - public long blobGasCost(final int blobCount) { - return BLOB_GAS_PER_BLOB * blobCount; + public long blobGasCost(final long blobCount) { + return getBlobGasPerBlob() * blobCount; + } + + @Override + public long getBlobGasPerBlob() { + return BLOB_GAS_PER_BLOB; } /** @@ -76,35 +81,4 @@ public class CancunGasCalculator extends ShanghaiGasCalculator { public long getTargetBlobGasPerBlock() { return TARGET_BLOB_GAS_PER_BLOCK; } - - /** - * Computes the excess blob gas for a given block based on the parent's excess blob gas and blob - * gas used. If the sum of parent's excess blob gas and parent's blob gas used is less than the - * target blob gas per block, the excess blob gas is calculated as 0. Otherwise, it is computed as - * the difference between the sum and the target blob gas per block. - * - * @param parentExcessBlobGas The excess blob gas of the parent block. - * @param newBlobs blob gas incurred by current block - * @return The excess blob gas for the current block. - */ - @Override - public long computeExcessBlobGas(final long parentExcessBlobGas, final int newBlobs) { - final long consumedBlobGas = blobGasCost(newBlobs); - final long currentExcessBlobGas = parentExcessBlobGas + consumedBlobGas; - - if (currentExcessBlobGas < TARGET_BLOB_GAS_PER_BLOCK) { - return 0L; - } - return currentExcessBlobGas - TARGET_BLOB_GAS_PER_BLOCK; - } - - @Override - public long computeExcessBlobGas(final long parentExcessBlobGas, final long parentBlobGasUsed) { - final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed; - - if (currentExcessBlobGas < TARGET_BLOB_GAS_PER_BLOCK) { - return 0L; - } - return currentExcessBlobGas - TARGET_BLOB_GAS_PER_BLOCK; - } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index 4c6770e70..049b197d0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -40,10 +40,12 @@ import org.hyperledger.besu.evm.precompile.SHA256PrecompiledContract; import org.hyperledger.besu.evm.processor.AbstractMessageProcessor; import java.util.List; +import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; +import org.apache.tuweni.units.bigints.UInt64; /** * Provides various gas cost lookups and calculations used during block processing. @@ -613,37 +615,51 @@ public interface GasCalculator { return 0L; } + /** + * Returns the blob gas cost per blob. This is the gas cost for each blob of data that is added to + * the block. + * + * @return the blob gas cost per blob + */ + default long getBlobGasPerBlob() { + return 0L; + } + /** * Return the gas cost given the number of blobs * * @param blobCount the number of blobs * @return the total gas cost */ - default long blobGasCost(final int blobCount) { + default long blobGasCost(final long blobCount) { return 0L; } /** - * Compute the new value for the excess blob gas, given the parent value and the count of new - * blobs + * Compute the new value for the excess blob gas, given the parent value, the parent blob gas used + * and the parent target blobs per block, if present. Used from Cancun onwards. Presence of + * parentTargetBlobsPerBlock implies EIP-7442/Prague enabled. Default to Cancun constant target + * gas value if parentTargetBlobsPerBlock is not present. * * @param parentExcessBlobGas excess blob gas from the parent - * @param newBlobs count of new blobs + * @param parentBlobGasUsed blob gas used from the parent + * @param parentTargetBlobsPerBlock the optional target blobs per block from the parent * @return the new excess blob gas value */ - default long computeExcessBlobGas(final long parentExcessBlobGas, final int newBlobs) { - return 0L; - } + default long computeExcessBlobGas( + final long parentExcessBlobGas, + final long parentBlobGasUsed, + final Optional parentTargetBlobsPerBlock) { + final long parentTargetBlobGas = + parentTargetBlobsPerBlock + .map(blobCount -> blobGasCost(blobCount.toLong())) + .orElse(CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK); + final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed; - /** - * Compute the new value for the excess blob gas, given the parent value and the blob gas used - * - * @param parentExcessBlobGas excess blob gas from the parent - * @param blobGasUsed blob gas used - * @return the new excess blob gas value - */ - default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) { - return 0L; + if (currentExcessBlobGas < parentTargetBlobGas) { + return 0L; + } + return currentExcessBlobGas - parentTargetBlobGas; } /** diff --git a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java index ded199bf3..b7a11395a 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java @@ -17,31 +17,37 @@ package org.hyperledger.besu.evm.gascalculator; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class CancunGasCalculatorTest { - private final CancunGasCalculator gasCalculator = new CancunGasCalculator(); + private final CancunGasCalculator cancunGasCalculator = new CancunGasCalculator(); @ParameterizedTest(name = "{index} - parent gas {0}, used gas {1}, new excess {2}") @MethodSource("blobGasses") public void shouldCalculateExcessBlobGasCorrectly( final long parentExcess, final long used, final long expected) { - assertThat(gasCalculator.computeExcessBlobGas(parentExcess, (int) used)).isEqualTo(expected); + final long usedBlobGas = cancunGasCalculator.blobGasCost(used); + assertThat( + cancunGasCalculator.computeExcessBlobGas(parentExcess, usedBlobGas, Optional.empty())) + .isEqualTo(expected); } - static Iterable blobGasses() { + Iterable blobGasses() { long targetGasPerBlock = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK; return List.of( Arguments.of(0L, 0L, 0L), Arguments.of(targetGasPerBlock, 0L, 0L), Arguments.of(0L, 3, 0L), Arguments.of(1, 3, 1), - Arguments.of(targetGasPerBlock, 1, CancunGasCalculator.BLOB_GAS_PER_BLOB), + Arguments.of(targetGasPerBlock, 1, cancunGasCalculator.getBlobGasPerBlob()), Arguments.of(targetGasPerBlock, 3, targetGasPerBlock)); } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java index c528dab64..0b9d0f237 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java @@ -18,13 +18,65 @@ import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.besu.datatypes.Address; -import org.junit.jupiter.api.Test; +import java.util.List; +import java.util.Optional; +import org.apache.tuweni.units.bigints.UInt64; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class PragueGasCalculatorTest { + + private final PragueGasCalculator pragueGasCalculator = new PragueGasCalculator(); + @Test void testPrecompileSize() { PragueGasCalculator subject = new PragueGasCalculator(); assertThat(subject.isPrecompile(Address.precompiled(0x14))).isFalse(); assertThat(subject.isPrecompile(Address.BLS12_MAP_FP2_TO_G2)).isTrue(); } + + @ParameterizedTest( + name = "{index} - parent gas {0}, used gas {1}, blob target {2} new excess {3}") + @MethodSource("blobGasses") + public void shouldCalculateExcessBlobGasCorrectly( + final long parentExcess, final long used, final long target, final long expected) { + final long usedBlobGas = pragueGasCalculator.blobGasCost(used); + assertThat( + pragueGasCalculator.computeExcessBlobGas( + parentExcess, usedBlobGas, Optional.of(UInt64.valueOf(target)))) + .isEqualTo(expected); + } + + Iterable blobGasses() { + long threeBlobTargetGas = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK; + long cancunTargetCount = 3; + long newTargetCount = 4; + + return List.of( + // If blob target count remains at 3 + Arguments.of(0L, 0L, cancunTargetCount, 0L), + Arguments.of(threeBlobTargetGas, 0L, cancunTargetCount, 0L), + Arguments.of(0L, cancunTargetCount, cancunTargetCount, 0L), + Arguments.of(1L, cancunTargetCount, cancunTargetCount, 1L), + Arguments.of( + threeBlobTargetGas, 1L, cancunTargetCount, pragueGasCalculator.getBlobGasPerBlob()), + Arguments.of(threeBlobTargetGas, 3L, cancunTargetCount, threeBlobTargetGas), + // New target count + Arguments.of(0L, 0L, newTargetCount, 0L), + Arguments.of(threeBlobTargetGas, 0L, newTargetCount, 0L), + Arguments.of(newTargetCount, 0L, newTargetCount, 0L), + Arguments.of(0L, newTargetCount, newTargetCount, 0L), + Arguments.of(1L, newTargetCount, newTargetCount, 1L), + Arguments.of( + pragueGasCalculator.blobGasCost(newTargetCount), + 1L, + newTargetCount, + pragueGasCalculator.getBlobGasPerBlob()), + Arguments.of(threeBlobTargetGas, newTargetCount, newTargetCount, threeBlobTargetGas)); + } } From cdfbe4633910061072d5d5d4bbb4ae869e7431c4 Mon Sep 17 00:00:00 2001 From: ahamlat Date: Thu, 5 Dec 2024 15:05:12 +0100 Subject: [PATCH 03/10] Clear updated and deleted accounts after each commit (#7974) Signed-off-by: Ameziane H. --- .../accumulator/DiffBasedWorldStateUpdateAccumulator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java index c28bb731b..37e9c9c58 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/worldview/accumulator/DiffBasedWorldStateUpdateAccumulator.java @@ -504,6 +504,8 @@ public abstract class DiffBasedWorldStateUpdateAccumulator Date: Thu, 5 Dec 2024 17:49:05 +0100 Subject: [PATCH 04/10] Improve newPayload and FCU logs (#7961) * Change the output log on newPayload and FCU executions Signed-off-by: Ameziane H. Co-authored-by: Fabio Di Fabio Co-authored-by: Simon Dudley --- CHANGELOG.md | 1 + .../org/hyperledger/besu/datatypes/Hash.java | 12 +++++++ .../org/hyperledger/besu/datatypes/Wei.java | 22 +++++++++++- .../hyperledger/besu/datatypes/WeiTest.java | 24 +++++++++++++ .../AbstractEngineForkchoiceUpdated.java | 9 +++-- .../engine/AbstractEngineNewPayload.java | 34 ++++++++++++++----- .../besu/ethereum/BlockProcessingResult.java | 25 ++++++++++++++ .../besu/ethereum/MainnetBlockValidator.java | 3 +- .../mainnet/AbstractBlockProcessor.java | 12 ++++++- ...lelizedConcurrentTransactionProcessor.java | 4 ++- .../TransactionProcessingResult.java | 21 ++++++++++++ 11 files changed, 149 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ee0947a3..629ef8e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ - Add RPC WS options to specify password file for keystore and truststore [#7970](https://github.com/hyperledger/besu/pull/7970) - Prometheus Java Metrics library upgraded to version 1.3.3 [#7880](https://github.com/hyperledger/besu/pull/7880) - Add histogram to Prometheus metrics system [#7944](https://github.com/hyperledger/besu/pull/7944) +- Improve newPayload and FCU logs [#7961](https://github.com/hyperledger/besu/pull/7961) ### Bug fixes diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Hash.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Hash.java index 3777169ed..53a19399a 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Hash.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Hash.java @@ -118,4 +118,16 @@ public class Hash extends DelegatingBytes32 { public static Hash fromHexStringLenient(final String str) { return new Hash(Bytes32.fromHexStringLenient(str)); } + + /*** + * For logging purposes, this method returns a shortened hex representation + * + * @return shortened string with only the beginning and the end of the hex representation + */ + public String toShortLogString() { + final var hexRepresentation = toFastHex(false); + String firstPart = hexRepresentation.substring(0, 5); + String lastPart = hexRepresentation.substring(hexRepresentation.length() - 5); + return firstPart + "....." + lastPart; + } } diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Wei.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Wei.java index 7b097a9dd..1fe2a5ffe 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Wei.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Wei.java @@ -161,11 +161,31 @@ public final class Wei extends BaseUInt256Value implements Quantity { * @return the string */ public String toHumanReadableString() { + return toHumanReadableStringWithPadding(1); + } + + /** + * Wei to human-readable string, with padding + * + * @return the string + */ + public String toHumanReadablePaddedString() { + return toHumanReadableStringWithPadding(6); + } + + /** + * Returns a human-readable String, the number of returned characters depends on the width + * parameter + * + * @param width the number of digits to use + * @return a human-readable String + */ + private String toHumanReadableStringWithPadding(final int width) { final BigInteger amount = toBigInteger(); final int numOfDigits = amount.toString().length(); final Unit preferredUnit = Unit.getPreferred(numOfDigits); final double res = amount.doubleValue() / preferredUnit.divisor; - return String.format("%1." + preferredUnit.decimals + "f %s", res, preferredUnit); + return String.format("%" + width + "." + preferredUnit.decimals + "f %s", res, preferredUnit); } /** The enum Unit. */ diff --git a/datatypes/src/test/java/org/hyperledger/besu/datatypes/WeiTest.java b/datatypes/src/test/java/org/hyperledger/besu/datatypes/WeiTest.java index 7162f98b0..39162fd2c 100644 --- a/datatypes/src/test/java/org/hyperledger/besu/datatypes/WeiTest.java +++ b/datatypes/src/test/java/org/hyperledger/besu/datatypes/WeiTest.java @@ -44,4 +44,28 @@ public class WeiTest { assertThat(Wei.of(new BigInteger("1" + String.valueOf(manyZeros))).toHumanReadableString()) .isEqualTo("100.00 tether"); } + + @Test + public void toHumanReadablePaddedString() { + assertThat(Wei.ZERO.toHumanReadablePaddedString()).isEqualTo(" 0 wei"); + assertThat(Wei.ONE.toHumanReadablePaddedString()).isEqualTo(" 1 wei"); + + assertThat(Wei.of(999).toHumanReadablePaddedString()).isEqualTo(" 999 wei"); + assertThat(Wei.of(1000).toHumanReadablePaddedString()).isEqualTo(" 1.00 kwei"); + + assertThat(Wei.of(1009).toHumanReadablePaddedString()).isEqualTo(" 1.01 kwei"); + assertThat(Wei.of(1011).toHumanReadablePaddedString()).isEqualTo(" 1.01 kwei"); + + assertThat(Wei.of(new BigInteger("1000000000")).toHumanReadablePaddedString()) + .isEqualTo(" 1.00 gwei"); + + assertThat(Wei.of(new BigInteger("1000000000000000000")).toHumanReadablePaddedString()) + .isEqualTo(" 1.00 ether"); + + final char[] manyZeros = new char[32]; + Arrays.fill(manyZeros, '0'); + assertThat( + Wei.of(new BigInteger("1" + String.valueOf(manyZeros))).toHumanReadablePaddedString()) + .isEqualTo("100.00 tether"); + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java index 11ec3d04c..727677aa8 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java @@ -389,8 +389,7 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso // fcU calls are synchronous, no need to make volatile private long lastFcuInfoLog = System.currentTimeMillis(); - private static final String logMessage = - "{} for fork-choice-update: head: {}, finalized: {}, safeBlockHash: {}"; + private static final String logMessage = "FCU({}) | head: {} | finalized: {} | safeBlockHash: {}"; private void logForkchoiceUpdatedCall( final EngineStatus status, final EngineForkchoiceUpdatedParameter forkChoice) { @@ -413,9 +412,9 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso LOG.info( logMessage, status.name(), - forkChoice.getHeadBlockHash(), - forkChoice.getFinalizedBlockHash(), - forkChoice.getSafeBlockHash()); + forkChoice.getHeadBlockHash().toShortLogString(), + forkChoice.getFinalizedBlockHash().toShortLogString(), + forkChoice.getSafeBlockHash().toShortLogString()); } private void logAtDebug( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java index 8562e140f..ac9dfe221 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java @@ -370,7 +370,8 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet .flatMap(Optional::stream) .mapToInt(List::size) .sum(), - (System.currentTimeMillis() - startTimeMs) / 1000.0); + (System.currentTimeMillis() - startTimeMs) / 1000.0, + executionResult.getNbParallelizedTransations()); return respondWith(reqId, blockParam, newBlockHeader.getHash(), VALID); } else { if (executionResult.causedBy().isPresent()) { @@ -564,26 +565,41 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet .collect(Collectors.toList())); } - private void logImportedBlockInfo(final Block block, final int blobCount, final double timeInS) { + private void logImportedBlockInfo( + final Block block, + final int blobCount, + final double timeInS, + final Optional nbParallelizedTransations) { final StringBuilder message = new StringBuilder(); - message.append("Imported #%,d / %d tx"); + final int nbTransactions = block.getBody().getTransactions().size(); + message.append("Imported #%,d (%s)|%5d tx"); final List messageArgs = new ArrayList<>( - List.of(block.getHeader().getNumber(), block.getBody().getTransactions().size())); + List.of( + block.getHeader().getNumber(), block.getHash().toShortLogString(), nbTransactions)); if (block.getBody().getWithdrawals().isPresent()) { - message.append(" / %d ws"); + message.append("|%3d ws"); messageArgs.add(block.getBody().getWithdrawals().get().size()); } - message.append(" / %d blobs / base fee %s / %,d (%01.1f%%) gas / (%s) in %01.3fs. Peers: %d"); + double mgasPerSec = (timeInS != 0) ? block.getHeader().getGasUsed() / (timeInS * 1_000_000) : 0; + message.append( + "|%2d blobs| base fee %s| gas used %,11d (%5.1f%%)| exec time %01.3fs| mgas/s %6.2f"); messageArgs.addAll( List.of( blobCount, - block.getHeader().getBaseFee().map(Wei::toHumanReadableString).orElse("N/A"), + block.getHeader().getBaseFee().map(Wei::toHumanReadablePaddedString).orElse("N/A"), block.getHeader().getGasUsed(), (block.getHeader().getGasUsed() * 100.0) / block.getHeader().getGasLimit(), - block.getHash().toHexString(), timeInS, - ethPeers.peerCount())); + mgasPerSec)); + if (nbParallelizedTransations.isPresent()) { + double parallelizedTxPercentage = + (double) (nbParallelizedTransations.get() * 100) / nbTransactions; + message.append("| parallel txs %5.1f%%"); + messageArgs.add(parallelizedTxPercentage); + } + message.append("| peers: %2d"); + messageArgs.add(ethPeers.peerCount()); LOG.info(String.format(message.toString(), messageArgs.toArray())); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockProcessingResult.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockProcessingResult.java index f34bd056c..77276caec 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockProcessingResult.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockProcessingResult.java @@ -26,6 +26,7 @@ public class BlockProcessingResult extends BlockValidationResult { private final Optional yield; private final boolean isPartial; + private Optional nbParallelizedTransations = Optional.empty(); /** A result indicating that processing failed. */ public static final BlockProcessingResult FAILED = new BlockProcessingResult("processing failed"); @@ -40,6 +41,21 @@ public class BlockProcessingResult extends BlockValidationResult { this.isPartial = false; } + /** + * A result indicating that processing was successful but incomplete. + * + * @param yield the outputs of processing a block + * @param nbParallelizedTransations potential number of parallelized transactions during block + * processing + */ + public BlockProcessingResult( + final Optional yield, + final Optional nbParallelizedTransations) { + this.yield = yield; + this.isPartial = false; + this.nbParallelizedTransations = nbParallelizedTransations; + } + /** * A result indicating that processing was successful but incomplete. * @@ -144,4 +160,13 @@ public class BlockProcessingResult extends BlockValidationResult { public Optional> getRequests() { return yield.flatMap(BlockProcessingOutputs::getRequests); } + + /** + * Returns an optional that contains the number of parallelized transactions (if there is any) + * + * @return Optional of parallelized transactions during the block execution + */ + public Optional getNbParallelizedTransations() { + return nbParallelizedTransations; + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java index 00766cdf5..c2925d651 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java @@ -183,7 +183,8 @@ public class MainnetBlockValidator implements BlockValidator { } return new BlockProcessingResult( - Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests))); + Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)), + result.getNbParallelizedTransations()); } } catch (MerkleTrieException ex) { context.getWorldStateArchive().heal(ex.getMaybeAddress(), ex.getLocation()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index 496652cdf..d6a483a95 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -135,6 +135,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { blockHashLookup, blobGasPrice); + boolean parallelizedTxFound = false; + int nbParallelTx = 0; for (int i = 0; i < transactions.size(); i++) { final Transaction transaction = transactions.get(i); if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) { @@ -181,6 +183,13 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { transactionReceiptFactory.create( transaction.getType(), transactionProcessingResult, worldState, currentGasUsed); receipts.add(transactionReceipt); + if (!parallelizedTxFound + && transactionProcessingResult.getIsProcessedInParallel().isPresent()) { + parallelizedTxFound = true; + nbParallelTx = 1; + } else if (transactionProcessingResult.getIsProcessedInParallel().isPresent()) { + nbParallelTx++; + } } if (blockHeader.getBlobGasUsed().isPresent() && currentBlobGasUsed != blockHeader.getBlobGasUsed().get()) { @@ -243,7 +252,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { } return new BlockProcessingResult( - Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests))); + Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)), + parallelizedTxFound ? Optional.of(nbParallelTx) : Optional.empty()); } protected Optional runBlockPreProcessing( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java index c6beaa2f4..a62cc1fff 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java @@ -251,8 +251,10 @@ public class ParallelizedConcurrentTransactionProcessor { blockAccumulator.importStateChangesFromSource(transactionAccumulator); - if (confirmedParallelizedTransactionCounter.isPresent()) + if (confirmedParallelizedTransactionCounter.isPresent()) { confirmedParallelizedTransactionCounter.get().inc(); + transactionProcessingResult.setIsProcessedInParallel(Optional.of(Boolean.TRUE)); + } return Optional.of(transactionProcessingResult); } else { blockAccumulator.importPriorStateFromSource(transactionAccumulator); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/processing/TransactionProcessingResult.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/processing/TransactionProcessingResult.java index eca28927b..a05ecf3d8 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/processing/TransactionProcessingResult.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/processing/TransactionProcessingResult.java @@ -49,6 +49,8 @@ public class TransactionProcessingResult private final Bytes output; + private Optional isProcessedInParallel = Optional.empty(); + private final ValidationResult validationResult; private final Optional revertReason; @@ -194,6 +196,25 @@ public class TransactionProcessingResult return validationResult; } + /** + * Set isProcessedInParallel to the value in parameter + * + * @param isProcessedInParallel new value of isProcessedInParallel + */ + public void setIsProcessedInParallel(final Optional isProcessedInParallel) { + this.isProcessedInParallel = isProcessedInParallel; + } + + /** + * Returns a flag that indicates if the transaction was executed in parallel + * + * @return Optional of Boolean, the value of the boolean is true if the transaction was executed + * in parallel + */ + public Optional getIsProcessedInParallel() { + return isProcessedInParallel; + } + /** * Returns the reason why a transaction was reverted (if applicable). * From 2be04cab71c63c920b9cc1f44ca88ad1be7ad4f5 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Thu, 5 Dec 2024 22:04:31 +0100 Subject: [PATCH 05/10] Fix flaky Clique test waiting for nodes to be fully connected (#7993) Signed-off-by: Fabio Di Fabio --- .../clique/CliqueMiningAcceptanceTest.java | 5 ++++ .../CliqueProposeRpcAcceptanceTest.java | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java index f2bae4458..fd8566e38 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java @@ -120,6 +120,11 @@ public class CliqueMiningAcceptanceTest extends AcceptanceTestBase { final BesuNode minerNode1, final BesuNode minerNode2, final BesuNode minerNode3) { cluster.start(minerNode1, minerNode2, minerNode3); + // verify nodes are fully connected otherwise blocks could not be propagated + minerNode1.verify(net.awaitPeerCount(2)); + minerNode2.verify(net.awaitPeerCount(2)); + minerNode3.verify(net.awaitPeerCount(2)); + // verify that we have started producing blocks waitForBlockHeight(minerNode1, 1); final var minerChainHead = minerNode1.execute(ethTransactions.block()); diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueProposeRpcAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueProposeRpcAcceptanceTest.java index 1674c551a..405f56fd9 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueProposeRpcAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueProposeRpcAcceptanceTest.java @@ -33,6 +33,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase { final BesuNode minerNode3 = besu.createCliqueNodeWithValidators("miner3", initialValidators); cluster.start(minerNode1, minerNode2, minerNode3); + waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3); + cluster.verify(clique.validatorsEqual(minerNode1, minerNode2)); minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3)); minerNode2.execute(cliqueTransactions.createAddProposal(minerNode3)); @@ -47,6 +49,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase { final BesuNode minerNode3 = besu.createCliqueNodeWithValidators("miner3", initialValidators); cluster.start(minerNode1, minerNode2, minerNode3); + waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3); + cluster.verify(clique.validatorsEqual(minerNode1, minerNode2, minerNode3)); final Condition cliqueValidatorsChanged = clique.awaitSignerSetChange(minerNode1); minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode3)); @@ -63,6 +67,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase { final BesuNode minerNode3 = besu.createCliqueNodeWithValidators("miner3", initialValidators); cluster.start(minerNode1, minerNode2, minerNode3); + waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3); + cluster.verify(clique.validatorsEqual(minerNode1, minerNode2)); minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3)); minerNode1.verify(blockchain.reachesHeight(minerNode1, 1)); @@ -76,6 +82,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase { final BesuNode minerNode3 = besu.createCliqueNode("miner3"); cluster.start(minerNode1, minerNode2, minerNode3); + waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3); + cluster.verify(clique.validatorsEqual(minerNode1, minerNode2, minerNode3)); minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode3)); minerNode1.verify(blockchain.reachesHeight(minerNode1, 1)); @@ -90,6 +98,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase { final BesuNode minerNode3 = besu.createCliqueNodeWithValidators("miner3", initialValidators); cluster.start(minerNode1, minerNode2, minerNode3); + waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3); + minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3)); minerNode1.verify(blockchain.reachesHeight(minerNode1, 1)); minerNode1.verify(blockchain.beneficiaryEquals(minerNode3)); @@ -100,4 +110,18 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase { minerNode1.verify(blockchain.beneficiaryEquals(minerNode2)); minerNode1.verify(clique.nonceVoteEquals(CLIQUE_NONCE_VOTE.DROP)); } + + private void waitForNodesConnectedAndInSync( + final BesuNode minerNode1, final BesuNode minerNode2, final BesuNode minerNode3) { + // verify nodes are fully connected otherwise blocks could not be propagated + minerNode1.verify(net.awaitPeerCount(2)); + minerNode2.verify(net.awaitPeerCount(2)); + minerNode3.verify(net.awaitPeerCount(2)); + + // verify that the miner started producing blocks and all other nodes are syncing from it + waitForBlockHeight(minerNode1, 1); + final var minerChainHead = minerNode1.execute(ethTransactions.block()); + minerNode2.verify(blockchain.minimumHeight(minerChainHead.getNumber().longValue())); + minerNode3.verify(blockchain.minimumHeight(minerChainHead.getNumber().longValue())); + } } From 6dfaaf37c2c35cdf282dcdd809d0a2847d681956 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Fri, 6 Dec 2024 07:26:34 +1000 Subject: [PATCH 06/10] remove (empty) versions.gradle (#7990) --- gradle/versions.gradle | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 gradle/versions.gradle diff --git a/gradle/versions.gradle b/gradle/versions.gradle deleted file mode 100644 index e69de29bb..000000000 From 1637cef865efbec50f80b69417a722c680d1f8e8 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Fri, 6 Dec 2024 10:15:20 +1000 Subject: [PATCH 07/10] [CONTRIBUTING] add trivial change clause (#7972) --- CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c13ea6785..8f2fdade3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,9 @@ Having the following accounts is necessary for contributing code/issues to Besu. * To ask questions or chat with us, join our [Discord](https://discord.com/invite/hyperledger) * To edit pages in our wiki, you'll need a [Linux Foundation (LF) account]. +### Guidelines for Non-Code and other Trivial Contributions +Please keep in mind that we do not accept non-code contributions like fixing comments, typos or some other trivial fixes. Although we appreciate the extra help, managing lots of these small contributions is unfeasible, and puts extra pressure in our continuous delivery systems (running all tests, etc). Feel free to open an issue pointing any of those errors, and we will batch them into a single change. + ### Useful contributing links * [I just have a quick question](https://wiki.hyperledger.org/display/BESU/I+just+have+a+quick+question) From 13fd24f064595f37f9187891798cc74cbc1a979a Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 6 Dec 2024 11:20:07 +1000 Subject: [PATCH 08/10] Update CHANGELOG.md for 24.12.0 release (#7996) Update CHANGELOG.md for 24.12.0 release Signed-off-by: Jason Frame --- CHANGELOG.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 629ef8e18..36ff4aa9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,25 @@ # Changelog -## [Unreleased] +## Unreleased + +### Breaking Changes + +### Upcoming Breaking Changes +- Plugin API will be deprecating the BesuContext interface to be replaced with the ServiceManager interface. +- `MetricSystem::createLabelledGauge` is deprecated and will be removed in a future release, replace it with `MetricSystem::createLabelledSuppliedGauge` +- k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release +- `--host-whitelist` has been deprecated in favor of `--host-allowlist` since 2020 and will be removed in a future release +- Sunsetting features - for more context on the reasoning behind the deprecation of these features, including alternative options, read [this blog post](https://www.lfdecentralizedtrust.org/blog/sunsetting-tessera-and-simplifying-hyperledger-besu) + - Tessera privacy + - Smart-contract-based (onchain) permissioning + - Proof of Work consensus + - Fast Sync + +### Additions and Improvements + +### Bug fixes + +## 24.12.0 ### Breaking Changes - Removed Retesteth rpc service and commands [#7833](https://github.com/hyperledger/besu/pull/7783) From a3592a71b1be23a18f45a0b5ad3d0972acb8b401 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Fri, 6 Dec 2024 19:07:50 +1100 Subject: [PATCH 09/10] Allow frontier simulation when Base Fee is present (#7965) Signed-off-by: Gabriel-Trintinalia --- CHANGELOG.md | 1 + .../london/EthEstimateGasIntegrationTest.java | 1 + .../transaction/TransactionSimulator.java | 63 ++++++++++++++----- .../transaction/TransactionSimulatorTest.java | 39 ++++++++++++ 4 files changed, 90 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36ff4aa9b..6aca416db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ ### Additions and Improvements ### Bug fixes +- Correct default parameters for frontier transactions in `eth_call` and `eth_estimateGas` [#7965](https://github.com/hyperledger/besu/pull/7965) ## 24.12.0 diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthEstimateGasIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthEstimateGasIntegrationTest.java index 5fde9ccec..7c8cb8a08 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthEstimateGasIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthEstimateGasIntegrationTest.java @@ -137,6 +137,7 @@ public class EthEstimateGasIntegrationTest { .withChainId(BLOCKCHAIN.getChainId().add(BigInteger.ONE)) .withFrom(Address.fromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")) .withTo(Address.fromHexString("0x8888f1f195afa192cfee860698584c030f4c9db1")) + .withMaxFeePerGas(Wei.ONE) .withValue(Wei.ONE) .build(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index 1c6140f1d..e4db6aafb 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -296,9 +296,6 @@ public class TransactionSimulator { final long simulationGasCap = calculateSimulationGasCap(callParams.getGasLimit(), blockHeaderToProcess.getGasLimit()); - final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO; - final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY; - final MainnetTransactionProcessor transactionProcessor = protocolSchedule.getByBlockHeader(blockHeaderToProcess).getTransactionProcessor(); @@ -322,8 +319,6 @@ public class TransactionSimulator { senderAddress, nonce, simulationGasCap, - value, - payload, blobGasPrice); if (maybeTransaction.isEmpty()) { return Optional.empty(); @@ -404,9 +399,11 @@ public class TransactionSimulator { final Address senderAddress, final long nonce, final long gasLimit, - final Wei value, - final Bytes payload, final Wei blobGasPrice) { + + final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO; + final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY; + final Transaction.Builder transactionBuilder = Transaction.builder() .nonce(nonce) @@ -437,18 +434,21 @@ public class TransactionSimulator { maxPriorityFeePerGas = callParams.getMaxPriorityFeePerGas().orElse(gasPrice); maxFeePerBlobGas = callParams.getMaxFeePerBlobGas().orElse(blobGasPrice); } - if (header.getBaseFee().isEmpty()) { + + if (shouldSetGasPrice(callParams, header)) { transactionBuilder.gasPrice(gasPrice); - } else if (protocolSchedule.getChainId().isPresent()) { + } + + if (shouldSetMaxFeePerGas(callParams, header)) { transactionBuilder.maxFeePerGas(maxFeePerGas).maxPriorityFeePerGas(maxPriorityFeePerGas); - } else { - return Optional.empty(); + } + + if (shouldSetBlobGasPrice(callParams)) { + transactionBuilder.maxFeePerBlobGas(maxFeePerBlobGas); } transactionBuilder.guessType(); - if (transactionBuilder.getTransactionType().supportsBlob()) { - transactionBuilder.maxFeePerBlobGas(maxFeePerBlobGas); - } + if (transactionBuilder.getTransactionType().requiresChainId()) { callParams .getChainId() @@ -489,4 +489,39 @@ public class TransactionSimulator { return Optional.of(worldState.get(address) != null); } + + private boolean shouldSetGasPrice(final CallParameter callParams, final BlockHeader header) { + if (header.getBaseFee().isEmpty()) { + return true; + } + + // if maxPriorityFeePerGas and maxFeePerGas are not set, use gasPrice + return callParams.getMaxPriorityFeePerGas().isEmpty() && callParams.getMaxFeePerGas().isEmpty(); + } + + private boolean shouldSetMaxFeePerGas(final CallParameter callParams, final BlockHeader header) { + if (protocolSchedule.getChainId().isEmpty()) { + return false; + } + + if (header.getBaseFee().isEmpty()) { + return false; + } + + if (shouldSetBlobGasPrice(callParams)) { + return true; + } + + // only set maxFeePerGas and maxPriorityFeePerGas if they are present, otherwise transaction + // will be considered EIP-1559 transaction even if the simulation is for a legacy transaction + return callParams.getMaxPriorityFeePerGas().isPresent() + || callParams.getMaxFeePerGas().isPresent(); + } + + private boolean shouldSetBlobGasPrice(final CallParameter callParams) { + if (protocolSchedule.getChainId().isEmpty()) { + return false; + } + return callParams.getBlobVersionedHashes().isPresent(); + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java index e0715e584..8406d1e4c 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java @@ -912,4 +912,43 @@ public class TransactionSimulatorTest { Optional.of(maxFeePerBlobGas), Optional.of(bwc.getVersionedHashes())); } + + @Test + public void shouldSimulateLegacyTransactionWhenBaseFeeNotZero() { + // tests that the transaction simulator will simulate a legacy transaction when the base fee is + // not zero + // and the transaction is a legacy transaction + + final CallParameter callParameter = legacyTransactionCallParameter(); + + final BlockHeader blockHeader = + blockHeaderTestFixture + .number(1L) + .stateRoot(Hash.ZERO) + .baseFeePerGas(Wei.of(7)) + .buildHeader(); + + mockBlockchainForBlockHeader(blockHeader); + mockWorldStateForAccount(blockHeader, callParameter.getFrom(), 1L); + + final Transaction expectedTransaction = + Transaction.builder() + .type(TransactionType.FRONTIER) + .nonce(1L) + .gasPrice(callParameter.getGasPrice()) + .gasLimit(blockHeader.getGasLimit()) + .to(callParameter.getTo()) + .sender(callParameter.getFrom()) + .value(callParameter.getValue()) + .payload(callParameter.getPayload()) + .signature(FAKE_SIGNATURE) + .build(); + mockProcessorStatusForTransaction(expectedTransaction, Status.SUCCESSFUL); + + final Optional result = + transactionSimulator.process(callParameter, 1L); + + verifyTransactionWasProcessed(expectedTransaction); + assertThat(result.get().isSuccessful()).isTrue(); + } } From f8e93bf544965d3de05a8770fab5a149278665f3 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Fri, 6 Dec 2024 13:50:38 +0100 Subject: [PATCH 10/10] Support rpc pending block tag when estimating gas (#7951) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 2 +- .../dsl/node/ThreadBesuNodeRunner.java | 7 +- .../controller/BesuControllerBuilder.java | 6 +- .../TransactionSimulationServiceImpl.java | 54 ++++--- .../CliqueMinerExecutorTest.java | 2 +- .../qbft/support/TestContextBuilder.java | 7 +- .../ValidatorContractController.java | 6 +- .../jsonrpc/JsonRpcTestMethodsFactory.java | 25 ++-- .../pojoadapter/BlockAdapterBase.java | 8 +- .../internal/methods/AbstractEstimateGas.java | 108 ++++++++++---- .../methods/AbstractTraceByBlock.java | 5 +- .../api/jsonrpc/internal/methods/EthCall.java | 17 +-- .../internal/methods/EthCreateAccessList.java | 70 +++------ .../internal/methods/EthEstimateGas.java | 61 ++------ .../internal/methods/TraceCallMany.java | 11 +- .../AbstractEthGraphQLHttpServiceTest.java | 2 + .../AbstractJsonRpcHttpServiceTest.java | 1 + .../methods/EthCreateAccessListTest.java | 48 +++++-- .../internal/methods/EthEstimateGasTest.java | 75 ++++++---- .../blockcreation/AbstractBlockCreator.java | 67 ++------- .../ethereum/core/BlockHeaderBuilder.java | 53 +++++++ .../ethereum/core/ProcessableBlockHeader.java | 21 +++ .../mainnet/TransactionValidationParams.java | 7 + .../transaction/TransactionSimulator.java | 134 ++++++++++++++---- .../transaction/TransactionSimulatorTest.java | 79 ++++++++--- ...rtContractPermissioningControllerTest.java | 4 +- ...rtContractPermissioningControllerTest.java | 4 +- .../tracing/AccessListOperationTracer.java | 5 +- plugin-api/build.gradle | 2 +- .../TransactionSimulationService.java | 91 +++++++++--- 30 files changed, 621 insertions(+), 361 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aca416db..e4486dfbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Fast Sync ### Additions and Improvements +- Proper support for `pending` block tag when calling `eth_estimateGas` and `eth_createAccessList` [#7951](https://github.com/hyperledger/besu/pull/7951) ### Bug fixes - Correct default parameters for frontier transactions in `eth_call` and `eth_estimateGas` [#7965](https://github.com/hyperledger/besu/pull/7965) @@ -68,7 +69,6 @@ - Add histogram to Prometheus metrics system [#7944](https://github.com/hyperledger/besu/pull/7944) - Improve newPayload and FCU logs [#7961](https://github.com/hyperledger/besu/pull/7961) - ### Bug fixes - Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825) - Fix CVE-2024-47535 [7878](https://github.com/hyperledger/besu/pull/7878) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index 091c1534d..3f0e0848c 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -396,9 +396,14 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner { final Blockchain blockchain, final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, + final MiningConfiguration miningConfiguration, final ApiConfiguration apiConfiguration) { return new TransactionSimulator( - blockchain, worldStateArchive, protocolSchedule, apiConfiguration.getGasCap()); + blockchain, + worldStateArchive, + protocolSchedule, + miningConfiguration, + apiConfiguration.getGasCap()); } @Provides diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index fdce6a08e..086850fa4 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -626,7 +626,11 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides transactionSimulator = new TransactionSimulator( - blockchain, worldStateArchive, protocolSchedule, apiConfiguration.getGasCap()); + blockchain, + worldStateArchive, + protocolSchedule, + miningConfiguration, + apiConfiguration.getGasCap()); final var consensusContext = createConsensusContext(blockchain, worldStateArchive, protocolSchedule); diff --git a/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java index 4dc411ee0..5244229c1 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.ethereum.chain.Blockchain; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; @@ -35,11 +34,6 @@ import java.util.Optional; /** TransactionSimulationServiceImpl */ @Unstable public class TransactionSimulationServiceImpl implements TransactionSimulationService { - private static final TransactionValidationParams SIMULATOR_ALLOWING_EXCEEDING_BALANCE = - ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()) - .isAllowExceedingBalance(true) - .build(); private Blockchain blockchain; private TransactionSimulator transactionSimulator; @@ -57,46 +51,50 @@ public class TransactionSimulationServiceImpl implements TransactionSimulationSe this.transactionSimulator = transactionSimulator; } - @Override - public Optional simulate( - final Transaction transaction, - final Hash blockHash, - final OperationTracer operationTracer, - final boolean isAllowExceedingBalance) { - return simulate( - transaction, Optional.empty(), blockHash, operationTracer, isAllowExceedingBalance); - } - @Override public Optional simulate( final Transaction transaction, final Optional maybeAccountOverrides, - final Hash blockHash, + final Optional maybeBlockHash, final OperationTracer operationTracer, final boolean isAllowExceedingBalance) { final CallParameter callParameter = CallParameter.fromTransaction(transaction); - final var maybeBlockHeader = - blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash)); + if (maybeBlockHash.isPresent()) { + final Hash blockHash = maybeBlockHash.get(); - if (maybeBlockHeader.isEmpty()) { - return Optional.of( - new TransactionSimulationResult( - transaction, - TransactionProcessingResult.invalid( - ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND)))); + final var maybeBlockHeader = + blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash)); + + if (maybeBlockHeader.isEmpty()) { + return Optional.of( + new TransactionSimulationResult( + transaction, + TransactionProcessingResult.invalid( + ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND)))); + } + + return transactionSimulator + .process( + callParameter, + isAllowExceedingBalance + ? TransactionValidationParams.transactionSimulatorAllowExceedingBalance() + : TransactionValidationParams.transactionSimulator(), + operationTracer, + maybeBlockHeader.get()) + .map(res -> new TransactionSimulationResult(transaction, res.result())); } return transactionSimulator - .process( + .processOnPending( callParameter, maybeAccountOverrides, isAllowExceedingBalance - ? SIMULATOR_ALLOWING_EXCEEDING_BALANCE + ? TransactionValidationParams.transactionSimulatorAllowExceedingBalance() : TransactionValidationParams.transactionSimulator(), operationTracer, - maybeBlockHeader.get()) + transactionSimulator.simulatePendingBlockHeader()) .map(res -> new TransactionSimulationResult(transaction, res.result())); } } diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java index 9c2bf04bc..377757a32 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java @@ -154,7 +154,7 @@ public class CliqueMinerExecutorTest { } @Test - public void extraDataForNonEpochBlocksDoesNotContainValidaors() { + public void extraDataForNonEpochBlocksDoesNotContainValidators() { final Bytes vanityData = generateRandomVanityData(); final MiningConfiguration miningConfiguration = createMiningConfiguration(vanityData); diff --git a/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java b/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java index cc7066f17..a9de97ae1 100644 --- a/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java +++ b/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java @@ -387,7 +387,7 @@ public class TestContextBuilder { final boolean useFixedBaseFee, final List qbftForks) { - final MiningConfiguration miningParams = + final MiningConfiguration miningConfiguration = ImmutableMiningConfiguration.builder() .mutableInitValues( MutableInitValues.builder() @@ -445,7 +445,8 @@ public class TestContextBuilder { final BftValidatorOverrides validatorOverrides = convertBftForks(qbftForks); final TransactionSimulator transactionSimulator = - new TransactionSimulator(blockChain, worldStateArchive, protocolSchedule, 0L); + new TransactionSimulator( + blockChain, worldStateArchive, protocolSchedule, miningConfiguration, 0L); final BlockValidatorProvider blockValidatorProvider = BlockValidatorProvider.forkingValidatorProvider( @@ -496,7 +497,7 @@ public class TestContextBuilder { protocolContext, protocolSchedule, forksSchedule, - miningParams, + miningConfiguration, localAddress, BFT_EXTRA_DATA_ENCODER, ethScheduler); diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validator/ValidatorContractController.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validator/ValidatorContractController.java index 5b561856e..de3e9cf17 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validator/ValidatorContractController.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validator/ValidatorContractController.java @@ -15,7 +15,6 @@ package org.hyperledger.besu.consensus.qbft.validator; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; @@ -94,10 +93,7 @@ public class ValidatorContractController { final CallParameter callParams = new CallParameter(null, contractAddress, -1, null, null, payload); final TransactionValidationParams transactionValidationParams = - ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()) - .isAllowExceedingBalance(true) - .build(); + TransactionValidationParams.transactionSimulatorAllowExceedingBalance(); return transactionSimulator.process( callParams, transactionValidationParams, OperationTracer.NO_TRACING, blockNumber); } diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java index f9918036e..63554577c 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java @@ -97,12 +97,13 @@ public class JsonRpcTestMethodsFactory { final BlockImporter blockImporter = protocolSpec.getBlockImporter(); blockImporter.importBlock(context, block, HeaderValidationMode.FULL); } + final var miningConfiguration = MiningConfiguration.newDefault(); this.blockchainQueries = - new BlockchainQueries( - protocolSchedule, blockchain, stateArchive, MiningConfiguration.newDefault()); + new BlockchainQueries(protocolSchedule, blockchain, stateArchive, miningConfiguration); this.transactionSimulator = - new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L); + new TransactionSimulator( + blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L); } public JsonRpcTestMethodsFactory( @@ -115,15 +116,14 @@ public class JsonRpcTestMethodsFactory { this.stateArchive = stateArchive; this.context = context; this.protocolSchedule = importer.getProtocolSchedule(); + final var miningConfiguration = MiningConfiguration.newDefault(); this.blockchainQueries = new BlockchainQueries( - importer.getProtocolSchedule(), - blockchain, - stateArchive, - MiningConfiguration.newDefault()); + importer.getProtocolSchedule(), blockchain, stateArchive, miningConfiguration); this.synchronizer = mock(Synchronizer.class); this.transactionSimulator = - new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L); + new TransactionSimulator( + blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L); } public JsonRpcTestMethodsFactory( @@ -138,14 +138,13 @@ public class JsonRpcTestMethodsFactory { this.context = context; this.synchronizer = synchronizer; this.protocolSchedule = importer.getProtocolSchedule(); + final var miningConfiguration = MiningConfiguration.newDefault(); this.blockchainQueries = new BlockchainQueries( - importer.getProtocolSchedule(), - blockchain, - stateArchive, - MiningConfiguration.newDefault()); + importer.getProtocolSchedule(), blockchain, stateArchive, miningConfiguration); this.transactionSimulator = - new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L); + new TransactionSimulator( + blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L); } public BlockchainQueries getBlockchainQueries() { 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 945727fda..f49fa0a4c 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 @@ -26,7 +26,6 @@ import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.LogWithMetadata; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; @@ -359,14 +358,9 @@ public class BlockAdapterBase extends AdapterBase { data, Optional.empty()); - ImmutableTransactionValidationParams.Builder transactionValidationParams = - ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()); - transactionValidationParams.isAllowExceedingBalance(true); - return transactionSimulator.process( param, - transactionValidationParams.build(), + TransactionValidationParams.transactionSimulatorAllowExceedingBalance(), OperationTracer.NO_TRACING, (mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult.map( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractEstimateGas.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractEstimateGas.java index 8e826caf9..dc61b3bb2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractEstimateGas.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractEstimateGas.java @@ -16,10 +16,12 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonCallParameterUtil.validateAndGetCallParams; +import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; @@ -28,6 +30,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorR import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.transaction.CallParameter; @@ -35,9 +38,12 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; +import org.hyperledger.besu.evm.tracing.OperationTracer; import java.util.Optional; +import com.google.common.annotations.VisibleForTesting; + public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod { private static final double SUB_CALL_REMAINING_GAS_RATIO = 65D / 64D; @@ -60,27 +66,24 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod { } } - protected Optional blockHeader(final long blockNumber) { - if (getBlockchainQueries().headBlockNumber() == blockNumber) { - // chain head header if cached, and we can return it form memory - return Optional.of(getBlockchainQueries().getBlockchain().getChainHeadHeader()); - } - return getBlockchainQueries().getBlockHeaderByNumber(blockNumber); - } + protected abstract Object simulate( + final JsonRpcRequestContext requestContext, + final CallParameter callParams, + final long gasLimit, + final TransactionSimulationFunction simulationFunction); - protected Optional validateBlockHeader( - final Optional maybeBlockHeader) { - if (maybeBlockHeader.isEmpty()) { - return Optional.of(RpcErrorType.BLOCK_NOT_FOUND); - } - - final var blockHeader = maybeBlockHeader.get(); - if (!getBlockchainQueries() - .getWorldStateArchive() - .isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) { - return Optional.of(RpcErrorType.WORLD_STATE_UNAVAILABLE); - } - return Optional.empty(); + @Override + protected Object pendingResult(final JsonRpcRequestContext requestContext) { + final JsonCallParameter jsonCallParameter = validateAndGetCallParams(requestContext); + final var validationParams = getTransactionValidationParams(jsonCallParameter); + final var maybeStateOverrides = getAddressAccountOverrideMap(requestContext); + final var pendingBlockHeader = transactionSimulator.simulatePendingBlockHeader(); + final TransactionSimulationFunction simulationFunction = + (cp, op) -> + transactionSimulator.processOnPending( + cp, maybeStateOverrides, validationParams, op, pendingBlockHeader); + return simulate( + requestContext, jsonCallParameter, pendingBlockHeader.getGasLimit(), simulationFunction); } @Override @@ -95,13 +98,43 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod { return resultByBlockHeader(requestContext, jsonCallParameter, maybeBlockHeader.get()); } - protected abstract Object resultByBlockHeader( + private Object resultByBlockHeader( final JsonRpcRequestContext requestContext, final JsonCallParameter jsonCallParameter, - final BlockHeader blockHeader); + final BlockHeader blockHeader) { + final var validationParams = getTransactionValidationParams(jsonCallParameter); + final var maybeStateOverrides = getAddressAccountOverrideMap(requestContext); + final TransactionSimulationFunction simulationFunction = + (cp, op) -> + transactionSimulator.process( + cp, maybeStateOverrides, validationParams, op, blockHeader); + return simulate( + requestContext, jsonCallParameter, blockHeader.getGasLimit(), simulationFunction); + } - protected CallParameter overrideGasLimitAndPrice( - final JsonCallParameter callParams, final long gasLimit) { + private Optional blockHeader(final long blockNumber) { + if (getBlockchainQueries().headBlockNumber() == blockNumber) { + // chain head header if cached, and we can return it form memory + return Optional.of(getBlockchainQueries().getBlockchain().getChainHeadHeader()); + } + return getBlockchainQueries().getBlockHeaderByNumber(blockNumber); + } + + private Optional validateBlockHeader(final Optional maybeBlockHeader) { + if (maybeBlockHeader.isEmpty()) { + return Optional.of(RpcErrorType.BLOCK_NOT_FOUND); + } + + final var blockHeader = maybeBlockHeader.get(); + if (!getBlockchainQueries() + .getWorldStateArchive() + .isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) { + return Optional.of(RpcErrorType.WORLD_STATE_UNAVAILABLE); + } + return Optional.empty(); + } + + protected CallParameter overrideGasLimit(final CallParameter callParams, final long gasLimit) { return new CallParameter( callParams.getChainId(), callParams.getFrom(), @@ -142,7 +175,7 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod { final ValidationResult validationResult = result.getValidationResult(); if (validationResult != null && !validationResult.isValid()) { - if (validationResult.getErrorMessage().length() > 0) { + if (!validationResult.getErrorMessage().isEmpty()) { return errorResponse(request, JsonRpcError.from(validationResult)); } return errorResponse( @@ -170,4 +203,29 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod { final JsonRpcRequestContext request, final JsonRpcError jsonRpcError) { return new JsonRpcErrorResponse(request.getRequest().getId(), jsonRpcError); } + + protected static TransactionValidationParams getTransactionValidationParams( + final JsonCallParameter callParams) { + final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE); + + return isAllowExceedingBalance + ? TransactionValidationParams.transactionSimulatorAllowExceedingBalance() + : TransactionValidationParams.transactionSimulator(); + } + + @VisibleForTesting + protected Optional getAddressAccountOverrideMap( + final JsonRpcRequestContext request) { + try { + return request.getOptionalParameter(2, AccountOverrideMap.class); + } catch (JsonRpcParameter.JsonRpcParameterException e) { + throw new InvalidJsonRpcRequestException( + "Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e); + } + } + + protected interface TransactionSimulationFunction { + Optional simulate( + CallParameter callParams, OperationTracer operationTracer); + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractTraceByBlock.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractTraceByBlock.java index 36991796b..b7ec1449e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractTraceByBlock.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractTraceByBlock.java @@ -33,7 +33,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.vm.VmT import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.debug.TraceOptions; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; @@ -117,9 +116,7 @@ public abstract class AbstractTraceByBlock extends AbstractBlockParameterMethod } protected TransactionValidationParams buildTransactionValidationParams() { - return ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()) - .build(); + return TransactionValidationParams.transactionSimulator(); } protected TraceOptions buildTraceOptions(final Set traceTypes) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java index ef5c8c7a6..39a5c7487 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java @@ -35,7 +35,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSucces import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; @@ -169,20 +168,18 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod { private TransactionValidationParams buildTransactionValidationParams( final BlockHeader header, final JsonCallParameter callParams) { - ImmutableTransactionValidationParams.Builder transactionValidationParams = - ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()); - + final boolean isAllowExceedingBalance; // if it is not set explicitly whether we want a strict check of the balance or not. this will // be decided according to the provided parameters if (callParams.isMaybeStrict().isEmpty()) { - transactionValidationParams.isAllowExceedingBalance( - isAllowExceedingBalanceAutoSelection(header, callParams)); + isAllowExceedingBalance = isAllowExceedingBalanceAutoSelection(header, callParams); + } else { - transactionValidationParams.isAllowExceedingBalance( - !callParams.isMaybeStrict().orElse(Boolean.FALSE)); + isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE); } - return transactionValidationParams.build(); + return isAllowExceedingBalance + ? TransactionValidationParams.transactionSimulatorAllowExceedingBalance() + : TransactionValidationParams.transactionSimulator(); } private boolean isAllowExceedingBalanceAutoSelection( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessList.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessList.java index 2b8e79a81..7d4ba313c 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessList.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessList.java @@ -18,13 +18,9 @@ import org.hyperledger.besu.datatypes.AccessListEntry; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.CreateAccessListResult; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; -import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; -import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; @@ -48,20 +44,24 @@ public class EthCreateAccessList extends AbstractEstimateGas { } @Override - protected Object resultByBlockHeader( + protected Object simulate( final JsonRpcRequestContext requestContext, - final JsonCallParameter jsonCallParameter, - final BlockHeader blockHeader) { - final AccessListSimulatorResult maybeResult = - processTransaction(jsonCallParameter, blockHeader); + final CallParameter callParams, + final long gasLimit, + final TransactionSimulationFunction simulationFunction) { + + final AccessListOperationTracer tracer = AccessListOperationTracer.create(); + final Optional firstResult = + simulationFunction.simulate(overrideGasLimit(callParams, gasLimit), tracer); + // if the call accessList is different from the simulation result, calculate gas and return - if (shouldProcessWithAccessListOverride(jsonCallParameter, maybeResult.tracer())) { + if (shouldProcessWithAccessListOverride(callParams, tracer)) { final AccessListSimulatorResult result = processTransactionWithAccessListOverride( - jsonCallParameter, blockHeader, maybeResult.tracer().getAccessList()); + callParams, gasLimit, tracer.getAccessList(), simulationFunction); return createResponse(requestContext, result); } else { - return createResponse(requestContext, maybeResult); + return createResponse(requestContext, new AccessListSimulatorResult(firstResult, tracer)); } } @@ -73,16 +73,8 @@ public class EthCreateAccessList extends AbstractEstimateGas { .orElseGet(() -> errorResponse(requestContext, RpcErrorType.INTERNAL_ERROR)); } - private TransactionValidationParams transactionValidationParams( - final boolean isAllowExceedingBalance) { - return ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()) - .isAllowExceedingBalance(isAllowExceedingBalance) - .build(); - } - private boolean shouldProcessWithAccessListOverride( - final JsonCallParameter parameters, final AccessListOperationTracer tracer) { + final CallParameter parameters, final AccessListOperationTracer tracer) { // if empty, transaction did not access any storage, does not need to reprocess if (tracer.getAccessList().isEmpty()) { @@ -107,39 +99,23 @@ public class EthCreateAccessList extends AbstractEstimateGas { : errorResponse(request, result); } - private AccessListSimulatorResult processTransaction( - final JsonCallParameter jsonCallParameter, final BlockHeader blockHeader) { - final TransactionValidationParams transactionValidationParams = - transactionValidationParams(!jsonCallParameter.isMaybeStrict().orElse(Boolean.FALSE)); - - final CallParameter callParams = - overrideGasLimitAndPrice(jsonCallParameter, blockHeader.getGasLimit()); - - final AccessListOperationTracer tracer = AccessListOperationTracer.create(); - final Optional result = - transactionSimulator.process(callParams, transactionValidationParams, tracer, blockHeader); - return new AccessListSimulatorResult(result, tracer); - } - private AccessListSimulatorResult processTransactionWithAccessListOverride( - final JsonCallParameter jsonCallParameter, - final BlockHeader blockHeader, - final List accessList) { - final TransactionValidationParams transactionValidationParams = - transactionValidationParams(!jsonCallParameter.isMaybeStrict().orElse(Boolean.FALSE)); + final CallParameter callParameter, + final long gasLimit, + final List accessList, + final TransactionSimulationFunction simulationFunction) { final AccessListOperationTracer tracer = AccessListOperationTracer.create(); - final CallParameter callParameter = - overrideAccessList(jsonCallParameter, blockHeader.getGasLimit(), accessList); + final CallParameter modifiedCallParameter = + overrideAccessList(callParameter, gasLimit, accessList); final Optional result = - transactionSimulator.process( - callParameter, transactionValidationParams, tracer, blockHeader); + simulationFunction.simulate(modifiedCallParameter, tracer); return new AccessListSimulatorResult(result, tracer); } - protected CallParameter overrideAccessList( - final JsonCallParameter callParams, + private CallParameter overrideAccessList( + final CallParameter callParams, final long gasLimit, final List accessListEntries) { return new CallParameter( @@ -151,7 +127,7 @@ public class EthCreateAccessList extends AbstractEstimateGas { callParams.getMaxFeePerGas(), callParams.getValue(), callParams.getPayload(), - Optional.ofNullable(accessListEntries)); + Optional.of(accessListEntries)); } private record AccessListSimulatorResult( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java index 2352a5a8c..05d5f78e0 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java @@ -14,19 +14,12 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; -import org.hyperledger.besu.datatypes.AccountOverrideMap; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; -import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; -import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; @@ -34,7 +27,6 @@ import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; import java.util.Optional; -import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,33 +44,17 @@ public class EthEstimateGas extends AbstractEstimateGas { } @Override - protected Object resultByBlockHeader( + protected Object simulate( final JsonRpcRequestContext requestContext, - final JsonCallParameter callParams, - final BlockHeader blockHeader) { - - final CallParameter modifiedCallParams = - overrideGasLimitAndPrice(callParams, blockHeader.getGasLimit()); - Optional maybeStateOverrides = getAddressAccountOverrideMap(requestContext); - // TODO implement for block overrides - - final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE); + final CallParameter callParams, + final long gasLimit, + final TransactionSimulationFunction simulationFunction) { final EstimateGasOperationTracer operationTracer = new EstimateGasOperationTracer(); - final var transactionValidationParams = - ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()) - .isAllowExceedingBalance(isAllowExceedingBalance) - .build(); - LOG.debug("Processing transaction with params: {}", modifiedCallParams); + LOG.debug("Processing transaction with params: {}", callParams); final var maybeResult = - transactionSimulator.process( - modifiedCallParams, - maybeStateOverrides, - transactionValidationParams, - operationTracer, - blockHeader); + simulationFunction.simulate(overrideGasLimit(callParams, gasLimit), operationTracer); final Optional maybeErrorResponse = validateSimulationResult(requestContext, maybeResult); @@ -89,12 +65,7 @@ public class EthEstimateGas extends AbstractEstimateGas { final var result = maybeResult.get(); long low = result.result().getEstimateGasUsedByTransaction(); final var lowResult = - transactionSimulator.process( - overrideGasLimitAndPrice(callParams, low), - maybeStateOverrides, - transactionValidationParams, - operationTracer, - blockHeader); + simulationFunction.simulate(overrideGasLimit(callParams, low), operationTracer); if (lowResult.isPresent() && lowResult.get().isSuccessful()) { return Quantity.create(low); @@ -106,12 +77,7 @@ public class EthEstimateGas extends AbstractEstimateGas { while (low + 1 < high) { mid = (low + high) / 2; var binarySearchResult = - transactionSimulator.process( - overrideGasLimitAndPrice(callParams, mid), - maybeStateOverrides, - transactionValidationParams, - operationTracer, - blockHeader); + simulationFunction.simulate(overrideGasLimit(callParams, mid), operationTracer); if (binarySearchResult.isEmpty() || !binarySearchResult.get().isSuccessful()) { low = mid; @@ -139,15 +105,4 @@ public class EthEstimateGas extends AbstractEstimateGas { } return Optional.empty(); } - - @VisibleForTesting - protected Optional getAddressAccountOverrideMap( - final JsonRpcRequestContext request) { - try { - return request.getOptionalParameter(2, AccountOverrideMap.class); - } catch (JsonRpcParameter.JsonRpcParameterException e) { - throw new InvalidJsonRpcRequestException( - "Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e); - } - } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java index 1601241db..aba51eb7a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java @@ -116,8 +116,7 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod { .getAndMapWorldState( blockHeader.getBlockHash(), ws -> { - final WorldUpdater updater = - transactionSimulator.getEffectiveWorldStateUpdater(blockHeader, ws); + final WorldUpdater updater = transactionSimulator.getEffectiveWorldStateUpdater(ws); try { Arrays.stream(transactionsAndTraceTypeParameters) .forEachOrdered( @@ -158,6 +157,11 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod { final Set traceTypes = traceTypeParameter.getTraceTypes(); final DebugOperationTracer tracer = new DebugOperationTracer(buildTraceOptions(traceTypes), false); + final var miningBeneficiary = + protocolSchedule + .getByBlockHeader(header) + .getMiningBeneficiaryCalculator() + .calculateBeneficiary(header); final Optional maybeSimulatorResult = transactionSimulator.processWithWorldUpdater( callParameter, @@ -165,7 +169,8 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod { buildTransactionValidationParams(), tracer, header, - worldUpdater); + worldUpdater, + miningBeneficiary); LOG.trace("Executing {} call for transaction {}", traceTypeParameter, callParameter); if (maybeSimulatorResult.isEmpty()) { diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java index 4aad65f43..967a803f9 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.chain.BadBlockManager; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.DefaultSyncStatus; +import org.hyperledger.besu.ethereum.core.ImmutableMiningConfiguration; import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.core.Transaction; @@ -141,6 +142,7 @@ public abstract class AbstractEthGraphQLHttpServiceTest { blockchain, blockchainSetupUtil.getWorldArchive(), blockchainSetupUtil.getProtocolSchedule(), + ImmutableMiningConfiguration.newDefault(), 0L); service = diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java index 75f84e133..0da11a035 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java @@ -175,6 +175,7 @@ public abstract class AbstractJsonRpcHttpServiceTest { blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive(), blockchainSetupUtil.getProtocolSchedule(), + miningConfiguration, 0L); return new JsonRpcMethodsFactory() diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessListTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessListTest.java index f7c0c3191..5046152a6 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessListTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessListTest.java @@ -72,6 +72,7 @@ public class EthCreateAccessListTest { @Mock private BlockHeader latestBlockHeader; @Mock private BlockHeader finalizedBlockHeader; @Mock private BlockHeader genesisBlockHeader; + @Mock private BlockHeader pendingBlockHeader; @Mock private Blockchain blockchain; @Mock private BlockchainQueries blockchainQueries; @Mock private TransactionSimulator transactionSimulator; @@ -93,6 +94,9 @@ public class EthCreateAccessListTest { when(blockchain.getChainHeadHeader()).thenReturn(latestBlockHeader); when(latestBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE); when(latestBlockHeader.getNumber()).thenReturn(2L); + when(pendingBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE); + when(pendingBlockHeader.getNumber()).thenReturn(3L); + when(transactionSimulator.simulatePendingBlockHeader()).thenReturn(pendingBlockHeader); when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(true); method = new EthCreateAccessList(blockchainQueries, transactionSimulator); @@ -139,6 +143,18 @@ public class EthCreateAccessListTest { assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse); } + @Test + public void pendingBlockTagEstimateOnPendingBlock() { + final JsonRpcRequestContext request = + ethCreateAccessListRequest(legacyTransactionCallParameter(Wei.ZERO), "pending"); + mockTransactionSimulatorResult(true, false, 1L, pendingBlockHeader); + + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse(null, new CreateAccessListResult(new ArrayList<>(), 1L)); + + assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse); + } + @Test public void shouldReturnGasEstimateErrorWhenGasPricePresentForEip1559Transaction() { final JsonRpcRequestContext request = @@ -186,7 +202,8 @@ public class EthCreateAccessListTest { mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader); assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse); - verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader)); + verify(transactionSimulator, times(1)) + .process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader)); } @Test @@ -207,7 +224,8 @@ public class EthCreateAccessListTest { assertThat(responseWithMockTracer(request, tracer)) .usingRecursiveComparison() .isEqualTo(expectedResponse); - verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(latestBlockHeader)); + verify(transactionSimulator, times(2)) + .process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader)); } @Test @@ -224,7 +242,8 @@ public class EthCreateAccessListTest { // Set TransactionSimulator.process response mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader); assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse); - verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader)); + verify(transactionSimulator, times(1)) + .process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader)); } @Test @@ -245,7 +264,8 @@ public class EthCreateAccessListTest { assertThat(responseWithMockTracer(request, tracer)) .usingRecursiveComparison() .isEqualTo(expectedResponse); - verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader)); + verify(transactionSimulator, times(1)) + .process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader)); } @Test @@ -269,7 +289,8 @@ public class EthCreateAccessListTest { assertThat(responseWithMockTracer(request, tracer)) .usingRecursiveComparison() .isEqualTo(expectedResponse); - verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(latestBlockHeader)); + verify(transactionSimulator, times(2)) + .process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader)); } @Test @@ -289,7 +310,8 @@ public class EthCreateAccessListTest { assertThat(responseWithMockTracer(request, tracer)) .usingRecursiveComparison() .isEqualTo(expectedResponse); - verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(finalizedBlockHeader)); + verify(transactionSimulator, times(2)) + .process(any(), eq(Optional.empty()), any(), any(), eq(finalizedBlockHeader)); } @Test @@ -309,7 +331,8 @@ public class EthCreateAccessListTest { assertThat(responseWithMockTracer(request, tracer)) .usingRecursiveComparison() .isEqualTo(expectedResponse); - verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(genesisBlockHeader)); + verify(transactionSimulator, times(2)) + .process(any(), eq(Optional.empty()), any(), any(), eq(genesisBlockHeader)); } private JsonRpcResponse responseWithMockTracer( @@ -328,14 +351,21 @@ public class EthCreateAccessListTest { return tracer; } + @SuppressWarnings("ReferenceEquality") private void mockTransactionSimulatorResult( final boolean isSuccessful, final boolean isReverted, final long estimateGas, final BlockHeader blockHeader) { final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class); - when(transactionSimulator.process(any(), any(), any(), eq(blockHeader))) - .thenReturn(Optional.of(mockTxSimResult)); + if (blockHeader == pendingBlockHeader) { + when(transactionSimulator.processOnPending( + any(), eq(Optional.empty()), any(), any(), eq(blockHeader))) + .thenReturn(Optional.of(mockTxSimResult)); + } else { + when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), eq(blockHeader))) + .thenReturn(Optional.of(mockTxSimResult)); + } final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class); when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas); when(mockResult.getRevertReason()) diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java index 7c770f08a..74c040824 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java @@ -38,7 +38,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; @@ -70,6 +69,7 @@ public class EthEstimateGasTest { @Mock private BlockHeader latestBlockHeader; @Mock private BlockHeader finalizedBlockHeader; @Mock private BlockHeader genesisBlockHeader; + @Mock private BlockHeader pendingBlockHeader; @Mock private Blockchain blockchain; @Mock private BlockchainQueries blockchainQueries; @Mock private TransactionSimulator transactionSimulator; @@ -91,6 +91,9 @@ public class EthEstimateGasTest { when(blockchain.getChainHeadHeader()).thenReturn(latestBlockHeader); when(latestBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE); when(latestBlockHeader.getNumber()).thenReturn(2L); + when(pendingBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE); + when(pendingBlockHeader.getNumber()).thenReturn(3L); + when(transactionSimulator.simulatePendingBlockHeader()).thenReturn(pendingBlockHeader); when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(true); method = new EthEstimateGas(blockchainQueries, transactionSimulator); @@ -376,11 +379,7 @@ public class EthEstimateGasTest { .process( eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)), eq(Optional.empty()), // no account overrides - eq( - ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()) - .isAllowExceedingBalance(true) - .build()), + eq(TransactionValidationParams.transactionSimulatorAllowExceedingBalance()), any(OperationTracer.class), eq(latestBlockHeader)); } @@ -397,11 +396,7 @@ public class EthEstimateGasTest { .process( eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)), eq(Optional.empty()), // no account overrides - eq( - ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()) - .isAllowExceedingBalance(false) - .build()), + eq(TransactionValidationParams.transactionSimulator()), any(OperationTracer.class), eq(latestBlockHeader)); } @@ -432,6 +427,17 @@ public class EthEstimateGasTest { assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse); } + @Test + public void pendingBlockTagEstimateOnPendingBlock() { + final JsonRpcRequestContext request = + ethEstimateGasRequest(eip1559TransactionCallParameter(), "pending"); + mockTransientProcessorResultGasEstimate(1L, true, false, pendingBlockHeader); + + final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L)); + + assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse); + } + @Test public void shouldUseBlockNumberParamWhenPresent() { final JsonRpcRequestContext request = @@ -488,6 +494,7 @@ public class EthEstimateGasTest { isSuccessful, estimateGas, gasPrice, revertReason, blockHeader); } + @SuppressWarnings("ReferenceEquality") private TransactionSimulatorResult getMockTransactionSimulatorResult( final boolean isSuccessful, final long estimateGas, @@ -495,21 +502,37 @@ public class EthEstimateGasTest { final Optional revertReason, final BlockHeader blockHeader) { final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class); - when(transactionSimulator.process( - eq(modifiedLegacyTransactionCallParameter(gasPrice)), - eq(Optional.empty()), // no account overrides - any(TransactionValidationParams.class), - any(OperationTracer.class), - eq(blockHeader))) - .thenReturn(Optional.of(mockTxSimResult)); - when(transactionSimulator.process( - eq(modifiedEip1559TransactionCallParameter()), - eq(Optional.empty()), // no account overrides - any(TransactionValidationParams.class), - any(OperationTracer.class), - eq(blockHeader))) - .thenReturn(Optional.of(mockTxSimResult)); - + if (blockHeader == pendingBlockHeader) { + when(transactionSimulator.processOnPending( + eq(modifiedLegacyTransactionCallParameter(gasPrice)), + eq(Optional.empty()), // no account overrides + any(TransactionValidationParams.class), + any(OperationTracer.class), + eq(blockHeader))) + .thenReturn(Optional.of(mockTxSimResult)); + when(transactionSimulator.processOnPending( + eq(modifiedEip1559TransactionCallParameter()), + eq(Optional.empty()), // no account overrides + any(TransactionValidationParams.class), + any(OperationTracer.class), + eq(blockHeader))) + .thenReturn(Optional.of(mockTxSimResult)); + } else { + when(transactionSimulator.process( + eq(modifiedLegacyTransactionCallParameter(gasPrice)), + eq(Optional.empty()), // no account overrides + any(TransactionValidationParams.class), + any(OperationTracer.class), + eq(blockHeader))) + .thenReturn(Optional.of(mockTxSimResult)); + when(transactionSimulator.process( + eq(modifiedEip1559TransactionCallParameter()), + eq(Optional.empty()), // no account overrides + any(TransactionValidationParams.class), + any(OperationTracer.class), + eq(blockHeader))) + .thenReturn(Optional.of(mockTxSimResult)); + } final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class); when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas); when(mockResult.getRevertReason()).thenReturn(revertReason); diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java index c4a3f89aa..50eb978df 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.blockcreation; +import static org.hyperledger.besu.ethereum.core.BlockHeaderBuilder.createPending; import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent; import org.hyperledger.besu.datatypes.Address; @@ -29,7 +30,6 @@ import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; -import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; @@ -41,15 +41,12 @@ import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor; import org.hyperledger.besu.ethereum.mainnet.BodyValidation; -import org.hyperledger.besu.ethereum.mainnet.DifficultyCalculator; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.WithdrawalsProcessor; -import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator; -import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.requests.ProcessRequestContext; import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessorCoordinator; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; @@ -60,7 +57,6 @@ import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleExcepti import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; -import java.math.BigInteger; import java.util.List; import java.util.Optional; import java.util.concurrent.CancellationException; @@ -198,12 +194,15 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator { protocolSchedule.getForNextBlockHeader(parentHeader, timestamp); final ProcessableBlockHeader processableBlockHeader = - createPendingBlockHeader( - timestamp, - maybePrevRandao, - maybeParentBeaconBlockRoot, - newProtocolSpec, - parentHeader); + createPending( + newProtocolSpec, + parentHeader, + miningConfiguration, + timestamp, + maybePrevRandao, + maybeParentBeaconBlockRoot) + .buildProcessableBlockHeader(); + final Address miningBeneficiary = miningBeneficiaryCalculator.getMiningBeneficiary(processableBlockHeader.getNumber()); @@ -421,52 +420,6 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator { return Lists.newArrayList(); } - private ProcessableBlockHeader createPendingBlockHeader( - final long timestamp, - final Optional maybePrevRandao, - final Optional maybeParentBeaconBlockRoot, - final ProtocolSpec protocolSpec, - final BlockHeader parentHeader) { - final long newBlockNumber = parentHeader.getNumber() + 1; - long gasLimit = - protocolSpec - .getGasLimitCalculator() - .nextGasLimit( - parentHeader.getGasLimit(), - miningConfiguration.getTargetGasLimit().orElse(parentHeader.getGasLimit()), - newBlockNumber); - - final DifficultyCalculator difficultyCalculator = protocolSpec.getDifficultyCalculator(); - final BigInteger difficulty = difficultyCalculator.nextDifficulty(timestamp, parentHeader); - - final Wei baseFee = - Optional.of(protocolSpec.getFeeMarket()) - .filter(FeeMarket::implementsBaseFee) - .map(BaseFeeMarket.class::cast) - .map( - feeMarket -> - feeMarket.computeBaseFee( - newBlockNumber, - parentHeader.getBaseFee().orElse(Wei.ZERO), - parentHeader.getGasUsed(), - feeMarket.targetGasUsed(parentHeader))) - .orElse(null); - - final Bytes32 prevRandao = maybePrevRandao.orElse(null); - final Bytes32 parentBeaconBlockRoot = maybeParentBeaconBlockRoot.orElse(null); - return BlockHeaderBuilder.create() - .parentHash(parentHeader.getHash()) - .coinbase(miningConfiguration.getCoinbase().orElseThrow()) - .difficulty(Difficulty.of(difficulty)) - .number(newBlockNumber) - .gasLimit(gasLimit) - .timestamp(timestamp) - .baseFee(baseFee) - .prevRandao(prevRandao) - .parentBeaconBlockRoot(parentBeaconBlockRoot) - .buildProcessableBlockHeader(); - } - @Override public void cancel() { isCancelled.set(true); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeaderBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeaderBuilder.java index fb464088b..381785995 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeaderBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeaderBuilder.java @@ -22,10 +22,14 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.mainnet.DifficultyCalculator; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.evm.log.LogsBloomFilter; import java.time.Instant; +import java.util.Optional; import java.util.OptionalLong; import org.apache.tuweni.bytes.Bytes; @@ -158,6 +162,55 @@ public class BlockHeaderBuilder { return toBuilder; } + public static BlockHeaderBuilder createPending( + final ProtocolSpec protocolSpec, + final BlockHeader parentHeader, + final MiningConfiguration miningConfiguration, + final long timestamp, + final Optional maybePrevRandao, + final Optional maybeParentBeaconBlockRoot) { + + final long newBlockNumber = parentHeader.getNumber() + 1; + final long gasLimit = + protocolSpec + .getGasLimitCalculator() + .nextGasLimit( + parentHeader.getGasLimit(), + miningConfiguration.getTargetGasLimit().orElse(parentHeader.getGasLimit()), + newBlockNumber); + + final DifficultyCalculator difficultyCalculator = protocolSpec.getDifficultyCalculator(); + final var difficulty = + Difficulty.of(difficultyCalculator.nextDifficulty(timestamp, parentHeader)); + + final Wei baseFee; + if (protocolSpec.getFeeMarket().implementsBaseFee()) { + final var baseFeeMarket = (BaseFeeMarket) protocolSpec.getFeeMarket(); + baseFee = + baseFeeMarket.computeBaseFee( + newBlockNumber, + parentHeader.getBaseFee().orElse(Wei.ZERO), + parentHeader.getGasUsed(), + baseFeeMarket.targetGasUsed(parentHeader)); + } else { + baseFee = null; + } + + final Bytes32 prevRandao = maybePrevRandao.orElse(null); + final Bytes32 parentBeaconBlockRoot = maybeParentBeaconBlockRoot.orElse(null); + + return BlockHeaderBuilder.create() + .parentHash(parentHeader.getHash()) + .coinbase(miningConfiguration.getCoinbase().orElseThrow()) + .difficulty(difficulty) + .number(newBlockNumber) + .gasLimit(gasLimit) + .timestamp(timestamp) + .baseFee(baseFee) + .prevRandao(prevRandao) + .parentBeaconBlockRoot(parentBeaconBlockRoot); + } + public BlockHeader buildBlockHeader() { validateBlockHeader(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ProcessableBlockHeader.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ProcessableBlockHeader.java index 2db290925..20650bc74 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ProcessableBlockHeader.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ProcessableBlockHeader.java @@ -196,4 +196,25 @@ public class ProcessableBlockHeader public String toLogString() { return getNumber() + " (time: " + getTimestamp() + ")"; } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("ProcessableBlockHeader{"); + sb.append("number=").append(number).append(", "); + sb.append("parentHash=").append(parentHash).append(", "); + sb.append("coinbase=").append(coinbase).append(", "); + sb.append("difficulty=").append(difficulty).append(", "); + sb.append("gasLimit=").append(gasLimit).append(", "); + sb.append("timestamp=").append(timestamp).append(", "); + sb.append("baseFee=").append(baseFee).append(", "); + sb.append("mixHashOrPrevRandao=").append(mixHashOrPrevRandao).append(", "); + if (parentBeaconBlockRoot != null) { + sb.append("parentBeaconBlockRoot=").append(parentBeaconBlockRoot).append(", "); + } + if (targetBlobsPerBlock != null) { + sb.append("targetBlobsPerBlock=").append(targetBlobsPerBlock); + } + return sb.append("}").toString(); + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java index be2185b09..78d649733 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidationParams.java @@ -35,6 +35,9 @@ public interface TransactionValidationParams { TransactionValidationParams transactionSimulatorParams = ImmutableTransactionValidationParams.of(false, false, false, false, false, true); + TransactionValidationParams transactionSimulatorAllowExceedingBalanceParams = + ImmutableTransactionValidationParams.of(false, true, false, false, false, true); + @Value.Default default boolean isAllowFutureNonce() { return false; @@ -69,6 +72,10 @@ public interface TransactionValidationParams { return transactionSimulatorParams; } + static TransactionValidationParams transactionSimulatorAllowExceedingBalance() { + return transactionSimulatorAllowExceedingBalanceParams; + } + static TransactionValidationParams processingBlock() { return processingBlockParams; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index e4db6aafb..c4558ab14 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -28,9 +28,10 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; +import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; @@ -84,16 +85,19 @@ public class TransactionSimulator { private final Blockchain blockchain; private final WorldStateArchive worldStateArchive; private final ProtocolSchedule protocolSchedule; + private final MiningConfiguration miningConfiguration; private final long rpcGasCap; public TransactionSimulator( final Blockchain blockchain, final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, + final MiningConfiguration miningConfiguration, final long rpcGasCap) { this.blockchain = blockchain; this.worldStateArchive = worldStateArchive; this.protocolSchedule = protocolSchedule; + this.miningConfiguration = miningConfiguration; this.rpcGasCap = rpcGasCap; } @@ -141,14 +145,82 @@ public class TransactionSimulator { blockHeader); } + public Optional processOnPending( + final CallParameter callParams, + final Optional maybeStateOverrides, + final TransactionValidationParams transactionValidationParams, + final OperationTracer operationTracer, + final ProcessableBlockHeader pendingBlockHeader) { + + try (final MutableWorldState disposableWorldState = + duplicateWorldStateAtParent(pendingBlockHeader.getParentHash())) { + WorldUpdater updater = getEffectiveWorldStateUpdater(disposableWorldState); + + // in order to trace the state diff we need to make sure that + // the world updater always has a parent + if (operationTracer instanceof DebugOperationTracer) { + updater = updater.parentUpdater().isPresent() ? updater : updater.updater(); + } + + return processWithWorldUpdater( + callParams, + maybeStateOverrides, + transactionValidationParams, + operationTracer, + pendingBlockHeader, + updater, + Address.ZERO); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public ProcessableBlockHeader simulatePendingBlockHeader() { + final long timestamp = System.currentTimeMillis(); + final var chainHeadHeader = blockchain.getChainHeadHeader(); + final ProtocolSpec protocolSpec = + protocolSchedule.getForNextBlockHeader(chainHeadHeader, timestamp); + + final var simulatedBlockHeader = + BlockHeaderBuilder.createPending( + protocolSpec, + chainHeadHeader, + miningConfiguration, + timestamp, + Optional.empty(), + Optional.empty()) + .buildProcessableBlockHeader(); + + LOG.trace("Simulated block header: {}", simulatedBlockHeader); + + return simulatedBlockHeader; + } + + private MutableWorldState duplicateWorldStateAtParent(final Hash parentHash) { + final var parentHeader = + blockchain + .getBlockHeader(parentHash) + .orElseThrow( + () -> + new IllegalStateException("Block with hash " + parentHash + " not available")); + + final Hash parentStateRoot = parentHeader.getStateRoot(); + return worldStateArchive + .getMutable(parentHeader, false) + .orElseThrow( + () -> + new IllegalArgumentException( + "World state not available for block " + + parentHeader.getNumber() + + " with state root " + + parentStateRoot)); + } + public Optional processAtHead(final CallParameter callParams) { final var chainHeadHash = blockchain.getChainHeadHash(); return process( callParams, - ImmutableTransactionValidationParams.builder() - .from(TransactionValidationParams.transactionSimulator()) - .isAllowExceedingBalance(true) - .build(), + TransactionValidationParams.transactionSimulatorAllowExceedingBalance(), OperationTracer.NO_TRACING, (mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult, blockchain @@ -209,7 +281,7 @@ public class TransactionSimulator { try (final MutableWorldState ws = getWorldState(header)) { - WorldUpdater updater = getEffectiveWorldStateUpdater(header, ws); + WorldUpdater updater = getEffectiveWorldStateUpdater(ws); // in order to trace the state diff we need to make sure that // the world updater always has a parent @@ -217,6 +289,12 @@ public class TransactionSimulator { updater = updater.parentUpdater().isPresent() ? updater : updater.updater(); } + final var miningBeneficiary = + protocolSchedule + .getByBlockHeader(header) + .getMiningBeneficiaryCalculator() + .calculateBeneficiary(header); + return preWorldStateCloseGuard.apply( ws, processWithWorldUpdater( @@ -225,7 +303,8 @@ public class TransactionSimulator { transactionValidationParams, operationTracer, header, - updater)); + updater, + miningBeneficiary)); } catch (final Exception e) { return Optional.empty(); @@ -267,21 +346,25 @@ public class TransactionSimulator { final Optional maybeStateOverrides, final TransactionValidationParams transactionValidationParams, final OperationTracer operationTracer, - final BlockHeader header, - final WorldUpdater updater) { - final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(header); + final ProcessableBlockHeader processableHeader, + final WorldUpdater updater, + final Address miningBeneficiary) { + final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(processableHeader); final Address senderAddress = callParams.getFrom() != null ? callParams.getFrom() : DEFAULT_FROM; - BlockHeader blockHeaderToProcess = header; - - if (transactionValidationParams.isAllowExceedingBalance() && header.getBaseFee().isPresent()) { + final ProcessableBlockHeader blockHeaderToProcess; + if (transactionValidationParams.isAllowExceedingBalance() + && processableHeader.getBaseFee().isPresent()) { blockHeaderToProcess = - BlockHeaderBuilder.fromHeader(header) + new BlockHeaderBuilder() + .populateFrom(processableHeader) .baseFee(Wei.ZERO) .blockHeaderFunctions(protocolSpec.getBlockHeaderFunctions()) - .buildBlockHeader(); + .buildProcessableBlockHeader(); + } else { + blockHeaderToProcess = processableHeader; } if (maybeStateOverrides.isPresent()) { for (Address accountToOverride : maybeStateOverrides.get().keySet()) { @@ -315,7 +398,7 @@ public class TransactionSimulator { buildTransaction( callParams, transactionValidationParams, - header, + processableHeader, senderAddress, nonce, simulationGasCap, @@ -330,9 +413,7 @@ public class TransactionSimulator { updater, blockHeaderToProcess, transaction, - protocolSpec - .getMiningBeneficiaryCalculator() - .calculateBeneficiary(blockHeaderToProcess), + miningBeneficiary, new CachingBlockHashLookup(blockHeaderToProcess, blockchain), false, transactionValidationParams, @@ -395,7 +476,7 @@ public class TransactionSimulator { private Optional buildTransaction( final CallParameter callParams, final TransactionValidationParams transactionValidationParams, - final BlockHeader header, + final ProcessableBlockHeader processableHeader, final Address senderAddress, final long nonce, final long gasLimit, @@ -435,11 +516,11 @@ public class TransactionSimulator { maxFeePerBlobGas = callParams.getMaxFeePerBlobGas().orElse(blobGasPrice); } - if (shouldSetGasPrice(callParams, header)) { + if (shouldSetGasPrice(callParams, processableHeader)) { transactionBuilder.gasPrice(gasPrice); } - if (shouldSetMaxFeePerGas(callParams, header)) { + if (shouldSetMaxFeePerGas(callParams, processableHeader)) { transactionBuilder.maxFeePerGas(maxFeePerGas).maxPriorityFeePerGas(maxPriorityFeePerGas); } @@ -463,8 +544,7 @@ public class TransactionSimulator { return Optional.ofNullable(transaction); } - public WorldUpdater getEffectiveWorldStateUpdater( - final BlockHeader header, final MutableWorldState publicWorldState) { + public WorldUpdater getEffectiveWorldStateUpdater(final MutableWorldState publicWorldState) { return publicWorldState.updater(); } @@ -490,7 +570,8 @@ public class TransactionSimulator { return Optional.of(worldState.get(address) != null); } - private boolean shouldSetGasPrice(final CallParameter callParams, final BlockHeader header) { + private boolean shouldSetGasPrice( + final CallParameter callParams, final ProcessableBlockHeader header) { if (header.getBaseFee().isEmpty()) { return true; } @@ -499,7 +580,8 @@ public class TransactionSimulator { return callParams.getMaxPriorityFeePerGas().isEmpty() && callParams.getMaxFeePerGas().isEmpty(); } - private boolean shouldSetMaxFeePerGas(final CallParameter callParams, final BlockHeader header) { + private boolean shouldSetMaxFeePerGas( + final CallParameter callParams, final ProcessableBlockHeader header) { if (protocolSchedule.getChainId().isEmpty()) { return false; } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java index 8406d1e4c..233d16ca2 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java @@ -15,8 +15,10 @@ package org.hyperledger.besu.ethereum.transaction; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.evm.tracing.OperationTracer.NO_TRACING; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -32,12 +34,14 @@ import org.hyperledger.besu.datatypes.BlobsWithCommitments; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlobTestFixture; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.Difficulty; +import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; @@ -52,7 +56,7 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult.Stat import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.math.BigInteger; @@ -99,10 +103,13 @@ public class TransactionSimulatorTest { @BeforeEach public void setUp() { + final var miningConfiguration = MiningConfiguration.newDefault().setCoinbase(Address.ZERO); this.transactionSimulator = - new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, 0); + new TransactionSimulator( + blockchain, worldStateArchive, protocolSchedule, miningConfiguration, 0); this.cappedTransactionSimulator = - new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, GAS_CAP); + new TransactionSimulator( + blockchain, worldStateArchive, protocolSchedule, miningConfiguration, GAS_CAP); } @Test @@ -182,6 +189,43 @@ public class TransactionSimulatorTest { verifyTransactionWasProcessed(expectedTransaction); } + @Test + public void simulateOnPendingBlockWorks() { + final CallParameter callParameter = eip1559TransactionCallParameter(); + + final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE); + + mockBlockchainForBlockHeader(blockHeader); + mockWorldStateForAccount(blockHeader, callParameter.getFrom(), 1L); + + final Transaction expectedTransaction = + Transaction.builder() + .type(TransactionType.EIP1559) + .chainId(BigInteger.ONE) + .nonce(1L) + .gasLimit(blockHeader.getGasLimit()) + .maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow()) + .maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow()) + .to(callParameter.getTo()) + .sender(callParameter.getFrom()) + .value(callParameter.getValue()) + .payload(callParameter.getPayload()) + .signature(FAKE_SIGNATURE) + .build(); + mockProcessorStatusForTransaction(expectedTransaction, Status.SUCCESSFUL); + + final Optional result = + transactionSimulator.processOnPending( + callParameter, + Optional.empty(), + TransactionValidationParams.transactionSimulator(), + NO_TRACING, + transactionSimulator.simulatePendingBlockHeader()); + + assertThat(result.get().isSuccessful()).isTrue(); + verifyTransactionWasProcessed(expectedTransaction); + } + @Test public void shouldSetGasPriceToZeroWhenExceedingBalanceAllowed() { final CallParameter callParameter = legacyTransactionCallParameter(Wei.ONE); @@ -209,7 +253,7 @@ public class TransactionSimulatorTest { transactionSimulator.process( callParameter, ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(true).build(), - OperationTracer.NO_TRACING, + NO_TRACING, 1L); verifyTransactionWasProcessed(expectedTransaction); @@ -245,7 +289,7 @@ public class TransactionSimulatorTest { transactionSimulator.process( callParameter, ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(true).build(), - OperationTracer.NO_TRACING, + NO_TRACING, 1L); verifyTransactionWasProcessed(expectedTransaction); @@ -279,7 +323,7 @@ public class TransactionSimulatorTest { transactionSimulator.process( callParameter, ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(false).build(), - OperationTracer.NO_TRACING, + NO_TRACING, 1L); verifyTransactionWasProcessed(expectedTransaction); @@ -314,7 +358,7 @@ public class TransactionSimulatorTest { transactionSimulator.process( callParameter, ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(false).build(), - OperationTracer.NO_TRACING, + NO_TRACING, 1L); verifyTransactionWasProcessed(expectedTransaction); @@ -600,10 +644,7 @@ public class TransactionSimulatorTest { // call process with original transaction cappedTransactionSimulator.process( - callParameter, - TransactionValidationParams.transactionSimulator(), - OperationTracer.NO_TRACING, - 1L); + callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L); // expect overwritten transaction to be processed verifyTransactionWasProcessed(expectedTransaction); @@ -638,10 +679,7 @@ public class TransactionSimulatorTest { // call process with original transaction cappedTransactionSimulator.process( - callParameter, - TransactionValidationParams.transactionSimulator(), - OperationTracer.NO_TRACING, - 1L); + callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L); // expect overwritten transaction to be processed verifyTransactionWasProcessed(expectedTransaction); @@ -677,10 +715,7 @@ public class TransactionSimulatorTest { // call process with original transaction cappedTransactionSimulator.process( - callParameter, - TransactionValidationParams.transactionSimulator(), - OperationTracer.NO_TRACING, - 1L); + callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L); // expect transaction with the original gas limit to be processed verifyTransactionWasProcessed(expectedTransaction); @@ -799,6 +834,8 @@ public class TransactionSimulatorTest { when(blockchain.getBlockHeader(blockHeader.getNumber())).thenReturn(Optional.of(blockHeader)); when(blockchain.getBlockHeader(blockHeader.getBlockHash())) .thenReturn(Optional.of(blockHeader)); + when(blockchain.getChainHeadHash()).thenReturn(blockHeader.getHash()); + when(blockchain.getChainHeadHeader()).thenReturn(blockHeader); } private void mockProtocolSpecForProcessWithWorldUpdater() { @@ -806,11 +843,15 @@ public class TransactionSimulatorTest { final BlockHashProcessor blockHashProcessor = mock(BlockHashProcessor.class); when(protocolSchedule.getChainId()).thenReturn(Optional.of(BigInteger.ONE)); when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec); + when(protocolSchedule.getForNextBlockHeader(any(), anyLong())).thenReturn(protocolSpec); when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor); when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase); when(protocolSpec.getBlockHeaderFunctions()).thenReturn(blockHeaderFunctions); when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0)); when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor); + when(protocolSpec.getGasCalculator()).thenReturn(new FrontierGasCalculator()); + when(protocolSpec.getGasLimitCalculator()).thenReturn(GasLimitCalculator.constant()); + when(protocolSpec.getDifficultyCalculator()).thenReturn((time, parent) -> BigInteger.TEN); } private void mockProcessorStatusForTransaction( diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningControllerTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningControllerTest.java index a9c97b1cc..fa0608a03 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningControllerTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningControllerTest.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.config.JsonUtil; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.chain.GenesisState; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; @@ -70,7 +71,8 @@ public class NodeSmartContractPermissioningControllerTest { genesisState.writeStateTo(worldArchive.getMutable()); final TransactionSimulator ts = - new TransactionSimulator(blockchain, worldArchive, protocolSchedule, 0L); + new TransactionSimulator( + blockchain, worldArchive, protocolSchedule, MiningConfiguration.newDefault(), 0L); final Address contractAddress = Address.fromHexString(contractAddressString); when(metricsSystem.createCounter( diff --git a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/TransactionSmartContractPermissioningControllerTest.java b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/TransactionSmartContractPermissioningControllerTest.java index 1fd4f58a4..d9746cfcf 100644 --- a/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/TransactionSmartContractPermissioningControllerTest.java +++ b/ethereum/permissioning/src/test/java/org/hyperledger/besu/ethereum/permissioning/TransactionSmartContractPermissioningControllerTest.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.chain.GenesisState; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.MiningConfiguration; import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; @@ -69,7 +70,8 @@ public class TransactionSmartContractPermissioningControllerTest { genesisState.writeStateTo(worldArchive.getMutable()); final TransactionSimulator ts = - new TransactionSimulator(blockchain, worldArchive, protocolSchedule, 0L); + new TransactionSimulator( + blockchain, worldArchive, protocolSchedule, MiningConfiguration.newDefault(), 0L); final Address contractAddress = Address.fromHexString(contractAddressString); when(metricsSystem.createCounter( diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/AccessListOperationTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/AccessListOperationTracer.java index 20b2db47c..a94c098bf 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/AccessListOperationTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/AccessListOperationTracer.java @@ -47,15 +47,16 @@ public class AccessListOperationTracer extends EstimateGasOperationTracer { * @return the access list */ public List getAccessList() { - final List list = new ArrayList<>(); if (warmedUpStorage != null && !warmedUpStorage.isEmpty()) { + final List list = new ArrayList<>(warmedUpStorage.size()); warmedUpStorage .rowMap() .forEach( (address, storageKeys) -> list.add(new AccessListEntry(address, new ArrayList<>(storageKeys.keySet())))); + return list; } - return list; + return List.of(); } /** diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 13b0912a9..f8608f893 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -71,7 +71,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 = 'dsbVupAvtmZlEeEeVDtk+VrzGFvyKxHgQntaMtOq5TY=' + knownHash = 'TPCo4SZ61OrJxRAa2SIcAIOAOjVTdRw+UOeHMuiJP84=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java index d5fc0a349..4736f099f 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java @@ -26,27 +26,14 @@ import java.util.Optional; /** Transaction simulation service interface */ @Unstable public interface TransactionSimulationService extends BesuService { - /** - * Simulate transaction execution at the block identified by the hash - * - * @param transaction tx - * @param blockHash the hash of the block - * @param operationTracer the tracer - * @param isAllowExceedingBalance should ignore the sender balance during the simulation? - * @return the result of the simulation - */ - Optional simulate( - Transaction transaction, - Hash blockHash, - OperationTracer operationTracer, - boolean isAllowExceedingBalance); /** - * Simulate transaction execution at the block identified by the hash + * Simulate transaction execution at the block identified by the hash if present, otherwise on the + * pending block, with optional state overrides that can be applied before the simulation. * * @param transaction tx * @param accountOverrides state overrides to apply to this simulation - * @param blockHash the hash of the block + * @param maybeBlockHash optional hash of the block, empty to simulate on pending block * @param operationTracer the tracer * @param isAllowExceedingBalance should ignore the sender balance during the simulation? * @return the result of the simulation @@ -54,7 +41,77 @@ public interface TransactionSimulationService extends BesuService { Optional simulate( Transaction transaction, Optional accountOverrides, - Hash blockHash, + Optional maybeBlockHash, OperationTracer operationTracer, boolean isAllowExceedingBalance); + + /** + * Simulate transaction execution at the block identified by the hash if present, otherwise on the + * pending block + * + * @param transaction tx + * @param maybeBlockHash optional hash of the block, empty to simulate on pending block + * @param operationTracer the tracer + * @param isAllowExceedingBalance should ignore the sender balance during the simulation? + * @return the result of the simulation + */ + default Optional simulate( + final Transaction transaction, + final Optional maybeBlockHash, + final OperationTracer operationTracer, + final boolean isAllowExceedingBalance) { + return simulate( + transaction, Optional.empty(), maybeBlockHash, operationTracer, isAllowExceedingBalance); + } + + /** + * Simulate transaction execution at the block identified by the hash + * + * @param transaction tx + * @param blockHash then hash of the block + * @param operationTracer the tracer + * @param isAllowExceedingBalance should ignore the sender balance during the simulation? + * @return the result of the simulation + * @deprecated use {@link #simulate(Transaction, Optional, OperationTracer, boolean)} + */ + @Deprecated(since = "24.12", forRemoval = true) + default Optional simulate( + final Transaction transaction, + final Hash blockHash, + final OperationTracer operationTracer, + final boolean isAllowExceedingBalance) { + return simulate( + transaction, + Optional.empty(), + Optional.of(blockHash), + operationTracer, + isAllowExceedingBalance); + } + + /** + * Simulate transaction execution at the block identified by the hash, with optional state + * overrides that can be applied before the simulation. + * + * @param transaction tx + * @param accountOverrides state overrides to apply to this simulation + * @param blockHash the hash of the block + * @param operationTracer the tracer + * @param isAllowExceedingBalance should ignore the sender balance during the simulation? + * @return the result of the simulation + * @deprecated use {@link #simulate(Transaction, Optional, Optional, OperationTracer, boolean)} + */ + @Deprecated(since = "24.12", forRemoval = true) + default Optional simulate( + final Transaction transaction, + final Optional accountOverrides, + final Hash blockHash, + final OperationTracer operationTracer, + final boolean isAllowExceedingBalance) { + return simulate( + transaction, + accountOverrides, + Optional.of(blockHash), + operationTracer, + isAllowExceedingBalance); + } }