diff --git a/.github/ISSUE_TEMPLATE/release-checklist.md b/.github/ISSUE_TEMPLATE/release-checklist.md index f484b7a590..9840a3fa49 100644 --- a/.github/ISSUE_TEMPLATE/release-checklist.md +++ b/.github/ISSUE_TEMPLATE/release-checklist.md @@ -8,8 +8,8 @@ assignees: '' --- - [ ] Confirm anything outstanding for release with other maintainers on #besu-release in Discord +- [ ] Update changelog if necessary, and merge a PR for it to main - [ ] Notify maintainers about updating changelog for in-flight PRs - - [ ] Update changelog if necessary, and merge a PR for it to main - [ ] Optional: for hotfixes, create a release branch and cherry-pick, e.g. `release--hotfix` - [ ] Optional: create a PR into main from the hotfix branch to see the CI checks pass - [ ] On the appropriate branch/commit, create a calver tag for the release candidate, format example: `24.4.0-RC2` @@ -18,18 +18,14 @@ assignees: '' - [ ] Sign off burn-in; convey burn-in results in #besu-release in Discord - [ ] Using the same git sha, create a calver tag for the FULL RELEASE, example format `24.4.0` - [ ] Using the FULL RELEASE tag, create a release in github to trigger the workflows. Once published: - - makes the release "latest" in github - this is now public and notifies subscribed users + - makes the release "latest" in github - publishes artefacts and version-specific docker tags - publishes the docker `latest` tag variants -- [ ] Draft homebrew PR -- [ ] Draft documentation release -- [ ] Ensure binary SHAs are correct on the release page -- [ ] Docker release startup test: - - `docker run hyperledger/besu:` - - `docker run hyperledger/besu:-arm64` - - `docker run --platform linux/amd64 hyperledger/besu:-amd64` - - `docker run --pull=always hyperledger/besu:latest` (check version is ) -- [ ] Merge homebrew PR -- [ ] Publish Docs Release +- [ ] Check binary SHAs are correct on the release page +- [ ] Check "Container Verify" GitHub workflow has run successfully +- [ ] Create homebrew release - run https://github.com/hyperledger/homebrew-besu/actions/workflows/update-version.yml +- [ ] Create besu-docs release - https://github.com/hyperledger/besu-docs/releases/new + - Copy release notes from besu + - If publishing the release in github doesn't automatically trigger this workflow, then manually run https://github.com/hyperledger/besu-docs/actions/workflows/update-version.yml - [ ] Social announcements diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d17eb2b6..4b391d7d4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,21 +2,39 @@ ## Next release +### Upcoming Breaking Changes +- Receipt compaction will be enabled by default in a future version of Besu. After this change it will not be possible to downgrade to the previous Besu version. +- --Xbonsai-limit-trie-logs-enabled is deprecated, use --bonsai-limit-trie-logs-enabled instead +- --Xbonsai-trie-logs-pruning-window-size is deprecated, use --bonsai-trie-logs-pruning-window-size instead +- `besu storage x-trie-log` subcommand is deprecated, use `besu storage trie-log` instead + ### Breaking Changes - Remove deprecated sync modes (X_SNAP and X_CHECKPOINT). Use SNAP and CHECKPOINT instead [#7309](https://github.com/hyperledger/besu/pull/7309) - Remove PKI-backed QBFT (deprecated in 24.5.1) Other forms of QBFT remain unchanged. [#7293](https://github.com/hyperledger/besu/pull/7293) +- Do not maintain connections to PoA bootnodes [#7358](https://github.com/hyperledger/besu/pull/7358). See [#7314](https://github.com/hyperledger/besu/pull/7314) for recommended alternative behaviour. - Added world state to traceEndBlock [#7386](https://github.com/hyperledger/besu/pull/7386) ### Additions and Improvements - `--Xsnapsync-bft-enabled` option enables experimental support for snap sync with IBFT/QBFT permissioned Bonsai-DB chains [#7140](https://github.com/hyperledger/besu/pull/7140) - Add support to load external profiles using `--profile` [#7265](https://github.com/hyperledger/besu/issues/7265) - `privacy-nonce-always-increments` option enables private transactions to always increment the nonce, even if the transaction is invalid [#6593](https://github.com/hyperledger/besu/pull/6593) -- Add `block-test` subcommand to the evmtool which runs blockchain reference tests [#7310](https://github.com/hyperledger/besu/pull/7310) +- Added `block-test` subcommand to the evmtool which runs blockchain reference tests [#7293](https://github.com/hyperledger/besu/pull/7293) +- removed PKI backed QBFT [#7310](https://github.com/hyperledger/besu/pull/7310) - Implement gnark-crypto for eip-2537 [#7316](https://github.com/hyperledger/besu/pull/7316) - Improve blob size transaction selector [#7312](https://github.com/hyperledger/besu/pull/7312) +- Added EIP-7702 [#7237](https://github.com/hyperledger/besu/pull/7237) +- Implement gnark-crypto for eip-196 [#7262](https://github.com/hyperledger/besu/pull/7262) +- Add trie log pruner metrics [#7352](https://github.com/hyperledger/besu/pull/7352) +- Force bonsai-limit-trie-logs-enabled=false when sync-mode=FULL instead of startup error [#7357](https://github.com/hyperledger/besu/pull/7357) +- `--Xbonsai-parallel-tx-processing-enabled` option enables executing transactions in parallel during block processing for Bonsai nodes +- Reduce default trie log pruning window size from 30,000 to 5,000 [#7365](https://github.com/hyperledger/besu/pull/7365) +- Add option `--poa-discovery-retry-bootnodes` for PoA networks to always use bootnodes during peer refresh, not just on first start [#7314](https://github.com/hyperledger/besu/pull/7314) ### Bug fixes - Fix `eth_call` deserialization to correctly ignore unknown fields in the transaction object. [#7323](https://github.com/hyperledger/besu/pull/7323) +- Prevent Besu from starting up with sync-mode=FULL and bonsai-limit-trie-logs-enabled=true for private networks [#7357](https://github.com/hyperledger/besu/pull/7357) +- Add 30 second timeout to trie log pruner preload [#7365](https://github.com/hyperledger/besu/pull/7365) +- Avoid executing pruner preload during trie log subcommands [#7366](https://github.com/hyperledger/besu/pull/7366) ## 24.7.0 diff --git a/acceptance-tests/tests/build.gradle b/acceptance-tests/tests/build.gradle index 1bc0e55567..953d1859e8 100644 --- a/acceptance-tests/tests/build.gradle +++ b/acceptance-tests/tests/build.gradle @@ -122,39 +122,11 @@ task acceptanceTest(type: Test) { doFirst { mkdir "${buildDir}/jvmErrorLogs" } } -task acceptanceTestMainnet(type: Test) { - inputs.property "integration.date", LocalTime.now() // so it runs at every invocation - exclude '**/bft/**' - exclude '**/clique/**' - exclude '**/permissioning/**' - exclude '**/privacy/**' - - useJUnitPlatform {} - - dependsOn(rootProject.installDist) - setSystemProperties(test.getSystemProperties()) - systemProperty 'acctests.runBesuAsProcess', 'true' - systemProperty 'java.security.properties', "${buildDir}/resources/test/acceptanceTesting.security" - mustRunAfter rootProject.subprojects*.test - description = 'Runs MAINNET Besu acceptance tests (excluding permissioning, privacy and some other stable features).' - group = 'verification' - - jvmArgs "-XX:ErrorFile=${buildDir}/jvmErrorLogs/java_err_pid%p.log" - - testLogging { - exceptionFormat = 'full' - showStackTraces = true - showStandardStreams = Boolean.getBoolean('acctests.showStandardStreams') - showExceptions = true - showCauses = true - } - - doFirst { mkdir "${buildDir}/jvmErrorLogs" } -} task acceptanceTestNotPrivacy(type: Test) { inputs.property "integration.date", LocalTime.now() // so it runs at every invocation exclude '**/privacy/**' + exclude '**/permissioning/**' exclude '**/bftsoak/**' useJUnitPlatform {} @@ -164,7 +136,7 @@ task acceptanceTestNotPrivacy(type: Test) { systemProperty 'acctests.runBesuAsProcess', 'true' systemProperty 'java.security.properties', "${buildDir}/resources/test/acceptanceTesting.security" mustRunAfter rootProject.subprojects*.test - description = 'Runs MAINNET Besu acceptance tests (excluding privacy since they run nightly, and are being refactored).' + description = 'Runs MAINNET Besu acceptance tests (excluding specific non-mainnet suites).' group = 'verification' jvmArgs "-XX:ErrorFile=${buildDir}/jvmErrorLogs/java_err_pid%p.log" diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/PragueAcceptanceTestHelper.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/PragueAcceptanceTestHelper.java new file mode 100644 index 0000000000..fa190f3de1 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/PragueAcceptanceTestHelper.java @@ -0,0 +1,193 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.tests.acceptance.ethereum; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.eth.EthTransactions; + +import java.io.IOException; +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.web3j.protocol.core.methods.response.EthBlock; + +public class PragueAcceptanceTestHelper { + protected static final MediaType MEDIA_TYPE_JSON = + MediaType.parse("application/json; charset=utf-8"); + + private final OkHttpClient httpClient; + private final ObjectMapper mapper; + private final BesuNode besuNode; + private final EthTransactions ethTransactions; + + private long blockTimeStamp = 0; + + PragueAcceptanceTestHelper(final BesuNode besuNode, final EthTransactions ethTransactions) { + this.besuNode = besuNode; + this.ethTransactions = ethTransactions; + httpClient = new OkHttpClient(); + mapper = new ObjectMapper(); + } + + public void buildNewBlock() throws IOException { + final EthBlock.Block block = besuNode.execute(ethTransactions.block()); + + blockTimeStamp += 1; + final Call buildBlockRequest = + createEngineCall(createForkChoiceRequest(block.getHash(), blockTimeStamp)); + + final String payloadId; + try (final Response buildBlockResponse = buildBlockRequest.execute()) { + payloadId = + mapper + .readTree(buildBlockResponse.body().string()) + .get("result") + .get("payloadId") + .asText(); + + assertThat(payloadId).isNotEmpty(); + } + + waitFor(500); + + final Call getPayloadRequest = createEngineCall(createGetPayloadRequest(payloadId)); + + final ObjectNode executionPayload; + final String newBlockHash; + final String parentBeaconBlockRoot; + try (final Response getPayloadResponse = getPayloadRequest.execute()) { + assertThat(getPayloadResponse.code()).isEqualTo(200); + + executionPayload = + (ObjectNode) + mapper + .readTree(getPayloadResponse.body().string()) + .get("result") + .get("executionPayload"); + + newBlockHash = executionPayload.get("blockHash").asText(); + parentBeaconBlockRoot = executionPayload.remove("parentBeaconBlockRoot").asText(); + + assertThat(newBlockHash).isNotEmpty(); + } + + final Call newPayloadRequest = + createEngineCall( + createNewPayloadRequest(executionPayload.toString(), parentBeaconBlockRoot)); + try (final Response newPayloadResponse = newPayloadRequest.execute()) { + assertThat(newPayloadResponse.code()).isEqualTo(200); + } + + final Call moveChainAheadRequest = createEngineCall(createForkChoiceRequest(newBlockHash)); + + try (final Response moveChainAheadResponse = moveChainAheadRequest.execute()) { + assertThat(moveChainAheadResponse.code()).isEqualTo(200); + } + } + + private Call createEngineCall(final String request) { + return httpClient.newCall( + new Request.Builder() + .url(besuNode.engineRpcUrl().get()) + .post(RequestBody.create(request, MEDIA_TYPE_JSON)) + .build()); + } + + private String createForkChoiceRequest(final String blockHash) { + return createForkChoiceRequest(blockHash, null); + } + + private String createForkChoiceRequest(final String parentBlockHash, final Long timeStamp) { + final Optional maybeTimeStamp = Optional.ofNullable(timeStamp); + + String forkChoiceRequest = + "{" + + " \"jsonrpc\": \"2.0\"," + + " \"method\": \"engine_forkchoiceUpdatedV3\"," + + " \"params\": [" + + " {" + + " \"headBlockHash\": \"" + + parentBlockHash + + "\"," + + " \"safeBlockHash\": \"" + + parentBlockHash + + "\"," + + " \"finalizedBlockHash\": \"" + + parentBlockHash + + "\"" + + " }"; + + if (maybeTimeStamp.isPresent()) { + forkChoiceRequest += + " ,{" + + " \"timestamp\": \"" + + Long.toHexString(maybeTimeStamp.get()) + + "\"," + + " \"prevRandao\": \"0x0000000000000000000000000000000000000000000000000000000000000000\"," + + " \"suggestedFeeRecipient\": \"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\"," + + " \"withdrawals\": []," + + " \"parentBeaconBlockRoot\": \"0x0000000000000000000000000000000000000000000000000000000000000000\"" + + " }"; + } + + forkChoiceRequest += " ]," + " \"id\": 67" + "}"; + + return forkChoiceRequest; + } + + private String createGetPayloadRequest(final String payloadId) { + return "{" + + " \"jsonrpc\": \"2.0\"," + + " \"method\": \"engine_getPayloadV4\"," + + " \"params\": [\"" + + payloadId + + "\"]," + + " \"id\": 67" + + "}"; + } + + private String createNewPayloadRequest( + final String executionPayload, final String parentBeaconBlockRoot) { + return "{" + + " \"jsonrpc\": \"2.0\"," + + " \"method\": \"engine_newPayloadV4\"," + + " \"params\": [" + + executionPayload + + ",[]," + + "\"" + + parentBeaconBlockRoot + + "\"" + + "]," + + " \"id\": 67" + + "}"; + } + + private static void waitFor(final long durationMilliSeconds) { + try { + Thread.sleep(durationMilliSeconds); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/SetCodeTransactionAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/SetCodeTransactionAcceptanceTest.java new file mode 100644 index 0000000000..f1b2558a97 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/SetCodeTransactionAcceptanceTest.java @@ -0,0 +1,125 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.tests.acceptance.ethereum; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.SetCodeAuthorization; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount; +import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.web3j.protocol.core.methods.response.TransactionReceipt; + +public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase { + private static final String GENESIS_FILE = "/dev/dev_prague.json"; + private static final SECP256K1 secp256k1 = new SECP256K1(); + + public static final Address SEND_ALL_ETH_CONTRACT_ADDRESS = + Address.fromHexStringStrict("0000000000000000000000000000000000009999"); + + private final Account authorizer = + accounts.createAccount( + Address.fromHexStringStrict("8da48afC965480220a3dB9244771bd3afcB5d895")); + public static final Bytes AUTHORIZER_PRIVATE_KEY = + Bytes.fromHexString("11f2e7b6a734ab03fa682450e0d4681d18a944f8b83c99bf7b9b4de6c0f35ea1"); + + private final Account transactionSponsor = + accounts.createAccount( + Address.fromHexStringStrict("a05b21E5186Ce93d2a226722b85D6e550Ac7D6E3")); + public static final Bytes TRANSACTION_SPONSOR_PRIVATE_KEY = + Bytes.fromHexString("3a4ff6d22d7502ef2452368165422861c01a0f72f851793b372b87888dc3c453"); + + private BesuNode besuNode; + private PragueAcceptanceTestHelper testHelper; + + @BeforeEach + void setUp() throws IOException { + besuNode = besu.createExecutionEngineGenesisNode("besuNode", GENESIS_FILE); + cluster.start(besuNode); + + testHelper = new PragueAcceptanceTestHelper(besuNode, ethTransactions); + } + + /** + * At the beginning of the test both the authorizer and the transaction sponsor have a balance of + * 90000 ETH. The authorizer creates an authorization for a contract that send all its ETH to any + * given address. The transaction sponsor created a 7702 transaction with it and sends all the ETH + * from the authorizer to itself. The authorizer balance should be 0 and the transaction sponsor + * balance should be 180000 ETH minus the transaction costs. + */ + @Test + public void shouldTransferAllEthOfAuthorizerToSponsor() throws IOException { + + // 7702 transaction + final org.hyperledger.besu.datatypes.SetCodeAuthorization authorization = + SetCodeAuthorization.builder() + .chainId(BigInteger.valueOf(20211)) + .address(SEND_ALL_ETH_CONTRACT_ADDRESS) + .signAndBuild( + secp256k1.createKeyPair( + secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger()))); + + final Transaction tx = + Transaction.builder() + .type(TransactionType.SET_CODE) + .chainId(BigInteger.valueOf(20211)) + .nonce(0) + .maxPriorityFeePerGas(Wei.of(1000000000)) + .maxFeePerGas(Wei.fromHexString("0x02540BE400")) + .gasLimit(1000000) + .to(Address.fromHexStringStrict(authorizer.getAddress())) + .value(Wei.ZERO) + .payload(Bytes32.leftPad(Bytes.fromHexString(transactionSponsor.getAddress()))) + .accessList(List.of()) + .setCodeTransactionPayloads(List.of(authorization)) + .signAndBuild( + secp256k1.createKeyPair( + secp256k1.createPrivateKey( + TRANSACTION_SPONSOR_PRIVATE_KEY.toUnsignedBigInteger()))); + + final String txHash = + besuNode.execute(ethTransactions.sendRawTransaction(tx.encoded().toHexString())); + testHelper.buildNewBlock(); + + Optional maybeTransactionReceipt = + besuNode.execute(ethTransactions.getTransactionReceipt(txHash)); + assertThat(maybeTransactionReceipt).isPresent(); + + cluster.verify(authorizer.balanceEquals(0)); + + final String gasPriceWithout0x = + maybeTransactionReceipt.get().getEffectiveGasPrice().substring(2); + final BigInteger txCost = + maybeTransactionReceipt.get().getGasUsed().multiply(new BigInteger(gasPriceWithout0x, 16)); + BigInteger expectedSponsorBalance = new BigInteger("180000000000000000000000").subtract(txCost); + cluster.verify(transactionSponsor.balanceEquals(Amount.wei(expectedSponsorBalance))); + } +} diff --git a/acceptance-tests/tests/src/test/resources/dev/dev_prague.json b/acceptance-tests/tests/src/test/resources/dev/dev_prague.json new file mode 100644 index 0000000000..26e59992d1 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/dev/dev_prague.json @@ -0,0 +1,112 @@ +{ + "config": { + "chainId":20211, + "homesteadBlock":0, + "eip150Block":0, + "eip155Block":0, + "eip158Block":0, + "byzantiumBlock":0, + "constantinopleBlock":0, + "petersburgBlock":0, + "istanbulBlock":0, + "muirGlacierBlock":0, + "berlinBlock":0, + "londonBlock":0, + "terminalTotalDifficulty":0, + "cancunTime":0, + "pragueTime":0, + "clique": { + "period": 5, + "epoch": 30000 + }, + "depositContractAddress": "0x4242424242424242424242424242424242424242" + }, + "nonce":"0x42", + "timestamp":"0x0", + "extraData":"0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit":"0x1C9C380", + "difficulty":"0x400000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase":"0x0000000000000000000000000000000000000000", + "alloc":{ + "a05b21E5186Ce93d2a226722b85D6e550Ac7D6E3": { + "privateKey": "3a4ff6d22d7502ef2452368165422861c01a0f72f851793b372b87888dc3c453", + "balance": "90000000000000000000000" + }, + "8da48afC965480220a3dB9244771bd3afcB5d895": { + "comment": "This account has signed a authorization for contract 0x0000000000000000000000000000000000009999 to send a 7702 transaction", + "privateKey": "11f2e7b6a734ab03fa682450e0d4681d18a944f8b83c99bf7b9b4de6c0f35ea1", + "balance": "90000000000000000000000" + }, + "0x0000000000000000000000000000000000009999": { + "comment": "Contract sends all its Ether to the address provided as a call data.", + "balance": "0", + "code": "5F5F5F5F475F355AF100", + "codeDecompiled": "PUSH0 PUSH0 PUSH0 PUSH0 SELFBALANCE PUSH0 CALLDATALOAD GAS CALL STOP", + "storage": {} + }, + "0xa4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb": { + "balance": "1000000000000000000000000000" + }, + "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f": { + "comment": "This is the account used to sign the transaction that creates a validator exit", + "balance": "1000000000000000000000000000" + }, + "0x00A3ca265EBcb825B45F985A16CEFB49958cE017": { + "comment": "This is the runtime bytecode for the Withdrawal Request Smart Contract. It was created from the deployment transaction in EIP-7002 (https://eips.ethereum.org/EIPS/eip-7002#deployment)", + "balance": "0", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b36603814156101215760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061012157600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460dd5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160a6565b910180921460ed579060025560f8565b90505f6002555f6003555b5f5460015460028282011161010f5750505f610115565b01600290035b5f555f600155604c025ff35b5f5ffd", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000004": "000000000000000000000000a4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb", + "0x0000000000000000000000000000000000000000000000000000000000000005": "b10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee9922355", + "0x0000000000000000000000000000000000000000000000000000000000000006": "5d8601f0cb3bcc4ce1af9864779a416e00000000000000000000000000000000" + } + }, + "0x4242424242424242424242424242424242424242": { + "comment": "The deposit contract", + "balance": "0", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212201dd26f37a621703009abf16e77e69c93dc50c79db7f6cc37543e3e0e3decdc9764736f6c634300060b0033", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x0000000000000000000000000000000000000000000000000000000000000024": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x0000000000000000000000000000000000000000000000000000000000000025": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x0000000000000000000000000000000000000000000000000000000000000026": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x0000000000000000000000000000000000000000000000000000000000000027": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x0000000000000000000000000000000000000000000000000000000000000028": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x0000000000000000000000000000000000000000000000000000000000000029": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x000000000000000000000000000000000000000000000000000000000000002a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x000000000000000000000000000000000000000000000000000000000000002b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x000000000000000000000000000000000000000000000000000000000000002c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x000000000000000000000000000000000000000000000000000000000000002d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x000000000000000000000000000000000000000000000000000000000000002e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x000000000000000000000000000000000000000000000000000000000000002f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x0000000000000000000000000000000000000000000000000000000000000030": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x0000000000000000000000000000000000000000000000000000000000000031": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x0000000000000000000000000000000000000000000000000000000000000032": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x0000000000000000000000000000000000000000000000000000000000000033": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x0000000000000000000000000000000000000000000000000000000000000034": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x0000000000000000000000000000000000000000000000000000000000000035": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x0000000000000000000000000000000000000000000000000000000000000036": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x0000000000000000000000000000000000000000000000000000000000000037": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x0000000000000000000000000000000000000000000000000000000000000038": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x0000000000000000000000000000000000000000000000000000000000000039": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x000000000000000000000000000000000000000000000000000000000000003a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x000000000000000000000000000000000000000000000000000000000000003b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x000000000000000000000000000000000000000000000000000000000000003c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x000000000000000000000000000000000000000000000000000000000000003d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x000000000000000000000000000000000000000000000000000000000000003e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x000000000000000000000000000000000000000000000000000000000000003f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + } + }, + "number":"0x0", + "gasUsed":"0x0", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas":"0x7" +} \ No newline at end of file diff --git a/besu/src/main/java/org/hyperledger/besu/Runner.java b/besu/src/main/java/org/hyperledger/besu/Runner.java index 4444f7acf7..eed35a03a9 100644 --- a/besu/src/main/java/org/hyperledger/besu/Runner.java +++ b/besu/src/main/java/org/hyperledger/besu/Runner.java @@ -253,7 +253,7 @@ public class Runner implements AutoCloseable { try { shutdown.await(); } catch (final InterruptedException e) { - LOG.debug("Interrupted while waiting for service " + serviceName + " to stop", e); + LOG.debug("Interrupted while waiting for service {} to stop {}", serviceName, e); Thread.currentThread().interrupt(); } } diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index 7ed627cfc1..d0c434fc81 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -195,6 +195,7 @@ public class RunnerBuilder { private boolean legacyForkIdEnabled; private Optional enodeDnsConfiguration; private List allowedSubnets = new ArrayList<>(); + private boolean poaDiscoveryRetryBootnodes = true; /** Instantiates a new Runner builder. */ public RunnerBuilder() {} @@ -603,6 +604,17 @@ public class RunnerBuilder { return this; } + /** + * Flag to indicate if peer table refreshes should always query bootnodes + * + * @param poaDiscoveryRetryBootnodes whether to always query bootnodes + * @return the runner builder + */ + public RunnerBuilder poaDiscoveryRetryBootnodes(final boolean poaDiscoveryRetryBootnodes) { + this.poaDiscoveryRetryBootnodes = poaDiscoveryRetryBootnodes; + return this; + } + /** * Build Runner instance. * @@ -625,6 +637,8 @@ public class RunnerBuilder { bootstrap = ethNetworkConfig.bootNodes(); } discoveryConfiguration.setBootnodes(bootstrap); + discoveryConfiguration.setIncludeBootnodesOnPeerRefresh( + besuController.getGenesisConfigOptions().isPoa() && poaDiscoveryRetryBootnodes); LOG.info("Resolved {} bootnodes.", bootstrap.size()); LOG.debug("Bootnodes = {}", bootstrap); discoveryConfiguration.setDnsDiscoveryURL(ethNetworkConfig.dnsDiscoveryUrl()); @@ -694,6 +708,7 @@ public class RunnerBuilder { final boolean fallbackEnabled = natMethod == NatMethod.AUTO || natMethodFallbackEnabled; final NatService natService = new NatService(buildNatManager(natMethod), fallbackEnabled); final NetworkBuilder inactiveNetwork = caps -> new NoopP2PNetwork(); + final NetworkBuilder activeNetwork = caps -> { return DefaultP2PNetwork.builder() @@ -792,20 +807,7 @@ public class RunnerBuilder { LOG.debug("added ethash observer: {}", stratumServer.get()); } - final Stream maintainedPeers; - if (besuController.getGenesisConfigOptions().isPoa()) { - // In a permissioned chain Besu should maintain connections to both static nodes and - // bootnodes, which includes retries periodically - maintainedPeers = - sanitizePeers( - network, - Stream.concat(staticNodes.stream(), bootnodes.stream()).collect(Collectors.toList())); - LOG.debug("Added bootnodes to the maintained peer list"); - } else { - // In a public chain only maintain connections to static nodes - maintainedPeers = sanitizePeers(network, staticNodes); - } - maintainedPeers + sanitizePeers(network, staticNodes) .map(DefaultPeer::fromEnodeURL) .forEach(peerNetwork::addMaintainedConnectionPeer); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 7216d9ebd6..d300fbea45 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -139,6 +139,7 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; +import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; import org.hyperledger.besu.evm.precompile.AbstractAltBnPrecompiledContract; import org.hyperledger.besu.evm.precompile.BigIntegerModularExponentiationPrecompiledContract; import org.hyperledger.besu.evm.precompile.KZGPointEvalPrecompiledContract; @@ -513,6 +514,19 @@ public class BesuCommand implements DefaultCommandValues, Runnable { } } + // Boolean option to set that in a PoA network the bootnodes should always be queried during + // peer table refresh. If this flag is disabled bootnodes are only sent FINDN requests on first + // startup, meaning that an offline bootnode or network outage at the client can prevent it + // discovering any peers without a restart. + @Option( + names = {"--poa-discovery-retry-bootnodes"}, + description = + "Always use of bootnodes for discovery in PoA networks. Disabling this reverts " + + " to the same behaviour as non-PoA networks, where neighbours are only discovered from bootnodes on first startup." + + "(default: ${DEFAULT-VALUE})", + arity = "1") + private final Boolean poaDiscoveryRetryBootnodes = true; + private Collection bannedNodeIds = new ArrayList<>(); // Used to discover the default IP of the client. @@ -1574,7 +1588,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { } private void validateDataStorageOptions() { - dataStorageOptions.validate(commandLine, syncMode); + dataStorageOptions.validate(commandLine); } private void validateRequiredOptions() { @@ -1901,6 +1915,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .privacyParameters(privacyParameters()) .clock(Clock.systemUTC()) .isRevertReasonEnabled(isRevertReasonEnabled) + .isParallelTxProcessingEnabled( + dataStorageConfiguration.getUnstable().isParallelTxProcessingEnabled()) .storageProvider(storageProvider) .gasLimitCalculator( miningParametersSupplier.get().getTargetGasLimit().isPresent() @@ -2231,10 +2247,41 @@ public class BesuCommand implements DefaultCommandValues, Runnable { return miningParameters; } - private DataStorageConfiguration getDataStorageConfiguration() { + /** + * Get the data storage configuration + * + * @return the data storage configuration + */ + public DataStorageConfiguration getDataStorageConfiguration() { if (dataStorageConfiguration == null) { dataStorageConfiguration = dataStorageOptions.toDomainObject(); } + + if (SyncMode.FULL.equals(getDefaultSyncModeIfNotSet()) + && DataStorageFormat.BONSAI.equals(dataStorageConfiguration.getDataStorageFormat()) + && dataStorageConfiguration.getBonsaiLimitTrieLogsEnabled()) { + + if (CommandLineUtils.isOptionSet( + commandLine, DataStorageOptions.BONSAI_LIMIT_TRIE_LOGS_ENABLED)) { + throw new ParameterException( + commandLine, + String.format( + "Cannot enable %s with --sync-mode=%s and --data-storage-format=%s. You must set %s or use a different sync-mode", + DataStorageOptions.BONSAI_LIMIT_TRIE_LOGS_ENABLED, + SyncMode.FULL, + DataStorageFormat.BONSAI, + DataStorageOptions.BONSAI_LIMIT_TRIE_LOGS_ENABLED + "=false")); + } + + dataStorageConfiguration = + ImmutableDataStorageConfiguration.copyOf(dataStorageConfiguration) + .withBonsaiLimitTrieLogsEnabled(false); + logger.warn( + "Forcing {}, since it cannot be enabled with --sync-mode={} and --data-storage-format={}.", + DataStorageOptions.BONSAI_LIMIT_TRIE_LOGS_ENABLED + "=false", + SyncMode.FULL, + DataStorageFormat.BONSAI); + } return dataStorageConfiguration; } @@ -2322,6 +2369,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { .rpcEndpointService(rpcEndpointServiceImpl) .enodeDnsConfiguration(getEnodeDnsConfiguration()) .allowedSubnets(p2PDiscoveryOptionGroup.allowedSubnets) + .poaDiscoveryRetryBootnodes(p2PDiscoveryOptionGroup.poaDiscoveryRetryBootnodes) .build(); addShutdownHook(runner); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java index 69cf819cb4..a0403c3942 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java @@ -24,7 +24,6 @@ import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration. import org.hyperledger.besu.cli.options.CLIOptions; import org.hyperledger.besu.cli.util.CommandLineUtils; -import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; @@ -63,7 +62,8 @@ public class DataStorageOptions implements CLIOptions arity = "1") private Long bonsaiMaxLayersToLoad = DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD; - private static final String BONSAI_LIMIT_TRIE_LOGS_ENABLED = "--bonsai-limit-trie-logs-enabled"; + /** The bonsai limit trie logs enabled option name */ + public static final String BONSAI_LIMIT_TRIE_LOGS_ENABLED = "--bonsai-limit-trie-logs-enabled"; /** The bonsai trie logs pruning window size. */ public static final String BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE = @@ -122,6 +122,14 @@ public class DataStorageOptions implements CLIOptions "Enables code storage using code hash instead of by account hash. (default: ${DEFAULT-VALUE})") private boolean bonsaiCodeUsingCodeHashEnabled = DEFAULT_BONSAI_CODE_USING_CODE_HASH_ENABLED; + @CommandLine.Option( + hidden = true, + names = {"--Xbonsai-parallel-tx-processing-enabled"}, + arity = "1", + description = + "Enables parallelization of transactions to optimize processing speed by concurrently loading and executing necessary data in advance. (default: ${DEFAULT-VALUE})") + private Boolean isParallelTxProcessingEnabled = false; + /** Default Constructor. */ Unstable() {} } @@ -139,43 +147,41 @@ public class DataStorageOptions implements CLIOptions * Validates the data storage options * * @param commandLine the full commandLine to check all the options specified by the user - * @param syncMode the sync mode */ - public void validate(final CommandLine commandLine, final SyncMode syncMode) { - if (DataStorageFormat.BONSAI == dataStorageFormat && bonsaiLimitTrieLogsEnabled) { - if (SyncMode.FULL == syncMode) { - throw new CommandLine.ParameterException( - commandLine, - String.format( - "Cannot enable %s with sync-mode %s. You must set %s or use a different sync-mode", - BONSAI_LIMIT_TRIE_LOGS_ENABLED, - SyncMode.FULL, - BONSAI_LIMIT_TRIE_LOGS_ENABLED + "=false")); - } - if (bonsaiMaxLayersToLoad < MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT) { - throw new CommandLine.ParameterException( - commandLine, - String.format( - BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD + " minimum value is %d", - MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT)); - } - if (bonsaiTrieLogPruningWindowSize <= 0) { - throw new CommandLine.ParameterException( - commandLine, - String.format( - BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + "=%d must be greater than 0", - bonsaiTrieLogPruningWindowSize)); + public void validate(final CommandLine commandLine) { + if (DataStorageFormat.BONSAI == dataStorageFormat) { + if (bonsaiLimitTrieLogsEnabled) { + if (bonsaiMaxLayersToLoad < MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT) { + throw new CommandLine.ParameterException( + commandLine, + String.format( + BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD + " minimum value is %d", + MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT)); + } + if (bonsaiTrieLogPruningWindowSize <= 0) { + throw new CommandLine.ParameterException( + commandLine, + String.format( + BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + "=%d must be greater than 0", + bonsaiTrieLogPruningWindowSize)); + } + if (bonsaiTrieLogPruningWindowSize <= bonsaiMaxLayersToLoad) { + throw new CommandLine.ParameterException( + commandLine, + String.format( + BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + + "=%d must be greater than " + + BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD + + "=%d", + bonsaiTrieLogPruningWindowSize, + bonsaiMaxLayersToLoad)); + } } - if (bonsaiTrieLogPruningWindowSize <= bonsaiMaxLayersToLoad) { + } else { + if (unstableOptions.isParallelTxProcessingEnabled) { throw new CommandLine.ParameterException( commandLine, - String.format( - BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE - + "=%d must be greater than " - + BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD - + "=%d", - bonsaiTrieLogPruningWindowSize, - bonsaiMaxLayersToLoad)); + "Transaction parallelization is not supported unless operating in a 'diffbased' mode, such as Bonsai."); } } } @@ -198,6 +204,8 @@ public class DataStorageOptions implements CLIOptions domainObject.getUnstable().getBonsaiFullFlatDbEnabled(); dataStorageOptions.unstableOptions.bonsaiCodeUsingCodeHashEnabled = domainObject.getUnstable().getBonsaiCodeStoredByCodeHashEnabled(); + dataStorageOptions.unstableOptions.isParallelTxProcessingEnabled = + domainObject.getUnstable().isParallelTxProcessingEnabled(); return dataStorageOptions; } @@ -214,6 +222,7 @@ public class DataStorageOptions implements CLIOptions ImmutableDataStorageConfiguration.Unstable.builder() .bonsaiFullFlatDbEnabled(unstableOptions.bonsaiFullFlatDbEnabled) .bonsaiCodeStoredByCodeHashEnabled(unstableOptions.bonsaiCodeUsingCodeHashEnabled) + .isParallelTxProcessingEnabled(unstableOptions.isParallelTxProcessingEnabled) .build()) .build(); } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommand.java index de42616822..e054bfb615 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommand.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogPruner; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; +import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; import java.io.IOException; @@ -82,7 +83,14 @@ public class TrieLogSubCommand implements Runnable { } private static BesuController createBesuController() { - return parentCommand.besuCommand.buildController(); + final DataStorageConfiguration config = parentCommand.besuCommand.getDataStorageConfiguration(); + // disable limit trie logs to avoid preloading during subcommand execution + return parentCommand + .besuCommand + .getControllerBuilder() + .dataStorageConfiguration( + ImmutableDataStorageConfiguration.copyOf(config).withBonsaiLimitTrieLogsEnabled(false)) + .build(); } @Command( 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 8f26909a51..ddb75fcded 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -203,6 +203,9 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides private int numberOfBlocksToCache = 0; + /** whether parallel transaction processing is enabled or not */ + protected boolean isParallelTxProcessingEnabled; + /** Instantiates a new Besu controller builder. */ protected BesuControllerBuilder() {} @@ -512,6 +515,20 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides return this; } + /** + * Sets whether parallel transaction processing is enabled. When parallel transaction processing + * is enabled, transactions within a block can be processed in parallel and potentially improving + * performance + * + * @param isParallelTxProcessingEnabled true to enable parallel transaction + * @return the besu controller + */ + public BesuControllerBuilder isParallelTxProcessingEnabled( + final boolean isParallelTxProcessingEnabled) { + this.isParallelTxProcessingEnabled = isParallelTxProcessingEnabled; + return this; + } + /** * Build besu controller. * @@ -793,7 +810,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides scheduler::executeServiceTask, dataStorageConfiguration.getBonsaiMaxLayersToLoad(), dataStorageConfiguration.getBonsaiTrieLogPruningWindowSize(), - isProofOfStake); + isProofOfStake, + metricsSystem); trieLogPruner.initialize(); return trieLogPruner; diff --git a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java index c127424069..b4ada60549 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java @@ -136,7 +136,9 @@ public class CliqueBesuControllerBuilder extends BesuControllerBuilder { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java index f669332202..0d0f8d2fd8 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java @@ -358,6 +358,15 @@ public class ConsensusScheduleBesuControllerBuilder extends BesuControllerBuilde return super.isRevertReasonEnabled(isRevertReasonEnabled); } + @Override + public BesuControllerBuilder isParallelTxProcessingEnabled( + final boolean isParallelTxProcessingEnabled) { + besuControllerBuilderSchedule + .values() + .forEach(b -> b.isParallelTxProcessingEnabled(isParallelTxProcessingEnabled)); + return super.isParallelTxProcessingEnabled(isParallelTxProcessingEnabled); + } + @Override public BesuControllerBuilder gasLimitCalculator(final GasLimitCalculator gasLimitCalculator) { besuControllerBuilderSchedule.values().forEach(b -> b.gasLimitCalculator(gasLimitCalculator)); diff --git a/besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java index 473549e0bd..b8d4d2645e 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/IbftBesuControllerBuilder.java @@ -291,7 +291,9 @@ public class IbftBesuControllerBuilder extends BftBesuControllerBuilder { bftExtraDataCodec().get(), evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/MainnetBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/MainnetBesuControllerBuilder.java index 27570483c1..e0fbed608a 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/MainnetBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/MainnetBesuControllerBuilder.java @@ -99,7 +99,9 @@ public class MainnetBesuControllerBuilder extends BesuControllerBuilder { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java index cdd46ea5b9..f5fc75959e 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java @@ -177,7 +177,9 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder { privacyParameters, isRevertReasonEnabled, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java index 795136ded5..ab2dbce3f1 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java @@ -329,7 +329,9 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder { bftExtraDataCodec().get(), evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java index cdf5413fff..d7b701b4c5 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java @@ -374,6 +374,13 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder { return propagateConfig(z -> z.isRevertReasonEnabled(isRevertReasonEnabled)); } + @Override + public BesuControllerBuilder isParallelTxProcessingEnabled( + final boolean isParallelTxProcessingEnabled) { + super.isParallelTxProcessingEnabled(isParallelTxProcessingEnabled); + return propagateConfig(z -> z.isParallelTxProcessingEnabled(isParallelTxProcessingEnabled)); + } + @Override public BesuControllerBuilder gasLimitCalculator(final GasLimitCalculator gasLimitCalculator) { super.gasLimitCalculator(gasLimitCalculator); diff --git a/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java b/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java index 6aa64ac510..ddf6940292 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java @@ -379,22 +379,20 @@ public class BesuPluginContextImpl implements BesuContext, PluginVersionsProvide plugin -> summary.add( String.format( - " - %s (Version: %s)", - plugin.getClass().getSimpleName(), plugin.getVersion()))); + " - %s (%s)", plugin.getClass().getSimpleName(), plugin.getVersion()))); } // Identify and log detected but not registered (skipped) plugins - List skippedPlugins = - detectedPlugins.stream() - .filter(plugin -> !registeredPlugins.contains(plugin)) - .map(plugin -> plugin.getClass().getSimpleName()) - .toList(); + List skippedPlugins = + detectedPlugins.stream().filter(plugin -> !registeredPlugins.contains(plugin)).toList(); if (!skippedPlugins.isEmpty()) { - summary.add("Skipped Plugins:"); + summary.add("Detected but not registered:"); skippedPlugins.forEach( - pluginName -> - summary.add(String.format(" - %s (Detected but not registered)", pluginName))); + plugin -> + summary.add( + String.format( + " - %s (%s)", plugin.getClass().getSimpleName(), plugin.getVersion()))); } summary.add( String.format( diff --git a/besu/src/test/java/org/hyperledger/besu/ForkIdsNetworkConfigTest.java b/besu/src/test/java/org/hyperledger/besu/ForkIdsNetworkConfigTest.java index fe5077acc6..28d4c77036 100644 --- a/besu/src/test/java/org/hyperledger/besu/ForkIdsNetworkConfigTest.java +++ b/besu/src/test/java/org/hyperledger/besu/ForkIdsNetworkConfigTest.java @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.mainnet.DefaultProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.util.Collection; import java.util.List; @@ -177,12 +178,21 @@ public class ForkIdsNetworkConfigTest { new MilestoneStreamingProtocolSchedule( (DefaultProtocolSchedule) MainnetProtocolSchedule.fromConfig( - configOptions, MiningParameters.MINING_DISABLED, new BadBlockManager())); + configOptions, + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem())); MilestoneStreamingProtocolSchedule postMergeProtocolSchedule = new MilestoneStreamingProtocolSchedule( (DefaultProtocolSchedule) MergeProtocolSchedule.create( - configOptions, false, MiningParameters.MINING_DISABLED, new BadBlockManager())); + configOptions, + false, + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem())); final MilestoneStreamingTransitionProtocolSchedule schedule = new MilestoneStreamingTransitionProtocolSchedule( preMergeProtocolSchedule, postMergeProtocolSchedule); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index efeff412d6..d0c42f6718 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -65,6 +65,7 @@ import org.hyperledger.besu.evm.precompile.KZGPointEvalPrecompiledContract; import org.hyperledger.besu.metrics.StandardMetricCategory; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.plugin.data.EnodeURL; +import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; import org.hyperledger.besu.util.number.Fraction; import org.hyperledger.besu.util.number.Percentage; import org.hyperledger.besu.util.number.PositiveNumber; @@ -804,6 +805,28 @@ public class BesuCommandTest extends CommandTestAbstract { assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } + @Test + public void poaDiscoveryRetryBootnodesValueTrueMustBeUsed() { + parseCommand("--poa-discovery-retry-bootnodes", "true"); + + verify(mockRunnerBuilder).poaDiscoveryRetryBootnodes(eq(true)); + verify(mockRunnerBuilder).build(); + + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + } + + @Test + public void poaDiscoveryRetryBootnodesValueFalseMustBeUsed() { + parseCommand("--poa-discovery-retry-bootnodes", "false"); + + verify(mockRunnerBuilder).poaDiscoveryRetryBootnodes(eq(false)); + verify(mockRunnerBuilder).build(); + + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + } + @Test public void callingWithBootnodesOptionButNoValueMustPassEmptyBootnodeList() { parseCommand("--bootnodes"); @@ -1281,13 +1304,34 @@ public class BesuCommandTest extends CommandTestAbstract { } @Test - public void parsesInvalidDefaultBonsaiLimitTrieLogsWhenFullSyncEnabled() { + public void bonsaiLimitTrieLogsDisabledWhenFullSyncEnabled() { parseCommand("--sync-mode=FULL"); + verify(mockControllerBuilder) + .dataStorageConfiguration(dataStorageConfigurationArgumentCaptor.capture()); + + final DataStorageConfiguration dataStorageConfiguration = + dataStorageConfigurationArgumentCaptor.getValue(); + assertThat(dataStorageConfiguration.getDataStorageFormat()).isEqualTo(BONSAI); + assertThat(dataStorageConfiguration.getBonsaiLimitTrieLogsEnabled()).isFalse(); + verify(mockLogger) + .warn( + "Forcing {}, since it cannot be enabled with --sync-mode={} and --data-storage-format={}.", + "--bonsai-limit-trie-logs-enabled=false", + SyncMode.FULL, + DataStorageFormat.BONSAI); + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + } + + @Test + public void parsesInvalidWhenFullSyncAndBonsaiLimitTrieLogsExplicitlyTrue() { + parseCommand("--sync-mode=FULL", "--bonsai-limit-trie-logs-enabled=true"); + Mockito.verifyNoInteractions(mockRunnerBuilder); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)) - .contains("Cannot enable --bonsai-limit-trie-logs-enabled with sync-mode FULL"); + .contains( + "Cannot enable --bonsai-limit-trie-logs-enabled with --sync-mode=FULL and --data-storage-format=BONSAI. You must set --bonsai-limit-trie-logs-enabled=false or use a different sync-mode"); } @Test diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index c92a3a5bda..c77ced5c6f 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -276,6 +276,8 @@ public abstract class CommandTestAbstract { when(mockControllerBuilder.privacyParameters(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.clock(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.isRevertReasonEnabled(false)).thenReturn(mockControllerBuilder); + when(mockControllerBuilder.isParallelTxProcessingEnabled(false)) + .thenReturn(mockControllerBuilder); when(mockControllerBuilder.storageProvider(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.gasLimitCalculator(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.requiredBlocks(any())).thenReturn(mockControllerBuilder); @@ -348,6 +350,7 @@ public abstract class CommandTestAbstract { when(mockRunnerBuilder.apiConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.enodeDnsConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.allowedSubnets(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.poaDiscoveryRetryBootnodes(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java b/besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java index c029983a33..4ee34295f4 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/options/TransactionPoolOptionsTest.java @@ -413,7 +413,7 @@ public class TransactionPoolOptionsTest @Test public void maxPrioritizedTxsPerTypeWrongTxType() { internalTestFailure( - "Invalid value for option '--tx-pool-max-prioritized-by-type' (MAP): expected one of [FRONTIER, ACCESS_LIST, EIP1559, BLOB] (case-insensitive) but was 'WRONG_TYPE'", + "Invalid value for option '--tx-pool-max-prioritized-by-type' (MAP): expected one of [FRONTIER, ACCESS_LIST, EIP1559, BLOB, SET_CODE] (case-insensitive) but was 'WRONG_TYPE'", "--tx-pool-max-prioritized-by-type", "WRONG_TYPE=1"); } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java b/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java index 5ab2757f88..2086381825 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java @@ -55,14 +55,6 @@ public class DataStorageOptionsTest "--bonsai-limit-trie-logs-enabled=false"); } - @Test - public void bonsaiTrieLogPruningWindowSizeShouldBePositive2() { - internalTestFailure( - "Cannot enable --bonsai-limit-trie-logs-enabled with sync-mode FULL. You must set --bonsai-limit-trie-logs-enabled=false or use a different sync-mode", - "--sync-mode", - "FULL"); - } - @Test public void bonsaiTrieLogPruningWindowSizeShouldBePositive() { internalTestFailure( diff --git a/besu/src/test/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommandTest.java new file mode 100644 index 0000000000..66536070a7 --- /dev/null +++ b/besu/src/test/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogSubCommandTest.java @@ -0,0 +1,74 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.cli.subcommands.storage; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; + +import org.hyperledger.besu.cli.CommandTestAbstract; +import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +class TrieLogSubCommandTest extends CommandTestAbstract { + + @Test + void limitTrieLogsDefaultDisabledForAllSubcommands() { + assertTrieLogSubcommand("prune"); + assertTrieLogSubcommand("count"); + assertTrieLogSubcommand("import"); + assertTrieLogSubcommand("export"); + } + + @Test + void limitTrieLogsDisabledForAllSubcommands() { + assertTrieLogSubcommandWithExplicitLimitEnabled("prune"); + assertTrieLogSubcommandWithExplicitLimitEnabled("count"); + assertTrieLogSubcommandWithExplicitLimitEnabled("import"); + assertTrieLogSubcommandWithExplicitLimitEnabled("export"); + } + + private void assertTrieLogSubcommand(final String trieLogSubcommand) { + parseCommand("storage", "trie-log", trieLogSubcommand); + assertConfigurationIsDisabledBySubcommand(); + } + + private void assertTrieLogSubcommandWithExplicitLimitEnabled(final String trieLogSubcommand) { + parseCommand("--bonsai-limit-trie-logs-enabled=true", "storage", "trie-log", trieLogSubcommand); + assertConfigurationIsDisabledBySubcommand(); + } + + private void assertConfigurationIsDisabledBySubcommand() { + verify(mockControllerBuilder, atLeastOnce()) + .dataStorageConfiguration(dataStorageConfigurationArgumentCaptor.capture()); + final List configs = + dataStorageConfigurationArgumentCaptor.getAllValues(); + assertThat(configs.get(0).getBonsaiLimitTrieLogsEnabled()).isTrue(); + assertThat(configs.get(1).getBonsaiLimitTrieLogsEnabled()).isFalse(); + } + + @Test + void limitTrieLogsDefaultEnabledForBesuMainCommand() { + parseCommand(); + verify(mockControllerBuilder, atLeastOnce()) + .dataStorageConfiguration(dataStorageConfigurationArgumentCaptor.capture()); + final List configs = + dataStorageConfigurationArgumentCaptor.getAllValues(); + assertThat(configs).allMatch(DataStorageConfiguration::getBonsaiLimitTrieLogsEnabled); + } +} diff --git a/besu/src/test/resources/complete_config.toml b/besu/src/test/resources/complete_config.toml index 3243b05613..8ae2105efd 100644 --- a/besu/src/test/resources/complete_config.toml +++ b/besu/src/test/resources/complete_config.toml @@ -6,6 +6,7 @@ data-path="/opt/besu" # Path # network discovery-enabled=false +poa-discovery-retry-bootnodes=true bootnodes=[ "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index a5e03bd599..27ea5c1645 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -29,6 +29,7 @@ nat-method="NONE" Xnat-kube-service-name="besu" Xnat-method-fallback-enabled=true discovery-enabled=false +poa-discovery-retry-bootnodes=true bootnodes=[ "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java index 91013ab97c..cd19755638 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java @@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; import java.util.HashMap; @@ -64,6 +65,9 @@ public class CliqueProtocolSchedule { * @param evmConfiguration the evm configuration * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled + * @param metricsSystem A metricSystem instance to be able to expose metrics in the underlying + * calls * @return the protocol schedule */ public static ProtocolSchedule create( @@ -74,7 +78,9 @@ public class CliqueProtocolSchedule { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { final CliqueConfigOptions cliqueConfig = config.getCliqueConfigOptions(); @@ -110,7 +116,9 @@ public class CliqueProtocolSchedule { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager) + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem) .createProtocolSchedule(); } @@ -124,6 +132,9 @@ public class CliqueProtocolSchedule { * @param evmConfiguration the evm configuration * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled + * @param metricsSystem A metricSystem instance to be able to expose metrics in the underlying + * calls * @return the protocol schedule */ @VisibleForTesting @@ -134,7 +145,9 @@ public class CliqueProtocolSchedule { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return create( config, forksSchedule, @@ -143,7 +156,9 @@ public class CliqueProtocolSchedule { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } private static ProtocolSpecBuilder applyCliqueSpecificModifications( diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java index 35f959e45b..5cbdbb6d06 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueProtocolScheduleTest.java @@ -37,6 +37,7 @@ import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.time.Instant; import java.util.List; @@ -68,7 +69,9 @@ public class CliqueProtocolScheduleTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final ProtocolSpec homesteadSpec = protocolSchedule.getByBlockHeader(blockHeader(1)); final ProtocolSpec tangerineWhistleSpec = protocolSchedule.getByBlockHeader(blockHeader(2)); @@ -92,7 +95,9 @@ public class CliqueProtocolScheduleTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .getByBlockHeader(blockHeader(0)); assertThat(homestead.getName()).isEqualTo("Frontier"); @@ -116,7 +121,9 @@ public class CliqueProtocolScheduleTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager())) + new BadBlockManager(), + false, + new NoOpMetricsSystem())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Epoch length in config must be greater than zero"); } @@ -136,7 +143,9 @@ public class CliqueProtocolScheduleTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager())) + new BadBlockManager(), + false, + new NoOpMetricsSystem())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Epoch length in config must be greater than zero"); } @@ -160,7 +169,9 @@ public class CliqueProtocolScheduleTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); BlockHeader emptyFrontierParent = headerBuilder diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java index 485b2aff79..ec10630df9 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java @@ -106,7 +106,9 @@ public class CliqueBlockCreatorTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final Address otherAddress = Util.publicKeyToAddress(otherKeyPair.getPublicKey()); validatorList.add(otherAddress); 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 bb69aaff48..9502d00a19 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 @@ -105,7 +105,9 @@ public class CliqueMinerExecutorTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); cliqueEthContext = mock(EthContext.class, RETURNS_DEEP_STUBS); blockHeaderBuilder = new BlockHeaderTestFixture(); } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilder.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilder.java index da81ce80b7..311bf30fae 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilder.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilder.java @@ -33,6 +33,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; import org.hyperledger.besu.ethereum.mainnet.WithdrawalsValidator; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; import java.util.HashMap; @@ -58,6 +59,9 @@ public abstract class BaseBftProtocolScheduleBuilder { * @param evmConfiguration the evm configuration * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. + * @param metricsSystem metricsSystem A metricSystem instance to be able to expose metrics in the + * underlying calls * @return the protocol schedule */ public BftProtocolSchedule createProtocolSchedule( @@ -68,7 +72,9 @@ public abstract class BaseBftProtocolScheduleBuilder { final BftExtraDataCodec bftExtraDataCodec, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { final Map> specMap = new HashMap<>(); forksSchedule @@ -90,7 +96,9 @@ public abstract class BaseBftProtocolScheduleBuilder { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager) + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem) .createProtocolSchedule(); return new BftProtocolSchedule((DefaultProtocolSchedule) protocolSchedule); } diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/CombinedProtocolScheduleFactoryTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/CombinedProtocolScheduleFactoryTest.java index 4e3cab78e7..02689d52af 100644 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/CombinedProtocolScheduleFactoryTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/CombinedProtocolScheduleFactoryTest.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; import java.util.List; @@ -177,7 +178,9 @@ public class CombinedProtocolScheduleFactoryTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); return new BftProtocolSchedule( (DefaultProtocolSchedule) protocolScheduleBuilder.createProtocolSchedule()); diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilderTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilderTest.java index 9b05bd7a36..e23664fd8d 100644 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilderTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/BaseBftProtocolScheduleBuilderTest.java @@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; import java.util.List; @@ -245,7 +246,9 @@ public class BaseBftProtocolScheduleBuilderTest { bftExtraDataCodec, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } private BftConfigOptions createBftConfig(final BigInteger blockReward) { diff --git a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java index 55a2f3551a..2d620c5632 100644 --- a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java +++ b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java @@ -333,7 +333,9 @@ public class TestContextBuilder { IBFT_EXTRA_DATA_ENCODER, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); ///////////////////////////////////////////////////////////////////////////////////// // From here down is BASICALLY taken from IbftBesuController diff --git a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleBuilder.java b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleBuilder.java index 5cad697c53..0789f2e898 100644 --- a/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleBuilder.java +++ b/consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleBuilder.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.util.Optional; @@ -46,6 +47,9 @@ public class IbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder * @param evmConfiguration the evm configuration * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled + * @param metricsSystem A metricSystem instance to be able to expose metrics in the underlying + * calls * @return the protocol schedule */ public static BftProtocolSchedule create( @@ -56,7 +60,9 @@ public class IbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder final BftExtraDataCodec bftExtraDataCodec, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return new IbftProtocolScheduleBuilder() .createProtocolSchedule( config, @@ -66,7 +72,9 @@ public class IbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder bftExtraDataCodec, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } /** @@ -78,6 +86,9 @@ public class IbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder * @param evmConfiguration the evm configuration * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. + * @param metricsSystem A metricSystem instance to be able to expose metrics in the underlying + * calls * @return the protocol schedule */ public static BftProtocolSchedule create( @@ -86,7 +97,9 @@ public class IbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder final BftExtraDataCodec bftExtraDataCodec, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return create( config, forksSchedule, @@ -95,7 +108,9 @@ public class IbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder bftExtraDataCodec, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } @Override diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java index 611399fd55..e5551ff3f3 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java @@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; import java.util.Collection; @@ -103,7 +104,9 @@ public class IbftProtocolScheduleTest { bftExtraDataCodec, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } private boolean validateHeader( diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java index 25c5c1d10b..1b86896f36 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java @@ -121,7 +121,9 @@ public class BftBlockCreatorTest { bftExtraDataEncoder, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final ProtocolContext protContext = new ProtocolContext( blockchain, diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java index 68e0ee5efe..b388b80a3f 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java @@ -79,6 +79,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.util.Subscribers; import java.math.BigInteger; @@ -184,7 +185,9 @@ public class IbftBlockHeightManagerTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); ProtocolSchedule protocolSchedule = new BftProtocolSchedule( diff --git a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeProtocolSchedule.java b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeProtocolSchedule.java index ff92319ae3..abbc3b130a 100644 --- a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeProtocolSchedule.java +++ b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeProtocolSchedule.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.evm.MainnetEVMs; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; import java.util.HashMap; @@ -49,19 +50,24 @@ public class MergeProtocolSchedule { * @param isRevertReasonEnabled the is revert reason enabled * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. * @return the protocol schedule */ public static ProtocolSchedule create( final GenesisConfigOptions config, final boolean isRevertReasonEnabled, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return create( config, PrivacyParameters.DEFAULT, isRevertReasonEnabled, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } /** @@ -72,6 +78,7 @@ public class MergeProtocolSchedule { * @param isRevertReasonEnabled the is revert reason enabled * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. * @return the protocol schedule */ public static ProtocolSchedule create( @@ -79,7 +86,9 @@ public class MergeProtocolSchedule { final PrivacyParameters privacyParameters, final boolean isRevertReasonEnabled, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { Map> postMergeModifications = new HashMap<>(); @@ -98,7 +107,9 @@ public class MergeProtocolSchedule { isRevertReasonEnabled, EvmConfiguration.DEFAULT, miningParameters, - badBlockManager) + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem) .createProtocolSchedule(); } diff --git a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java index faab15dac4..8600344cef 100644 --- a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java +++ b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; import java.util.Optional; @@ -65,17 +66,30 @@ public class TransitionProtocolSchedule implements ProtocolSchedule { * milestone starting points * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. * @return an initialised TransitionProtocolSchedule using post-merge defaults */ public static TransitionProtocolSchedule fromConfig( final GenesisConfigOptions genesisConfigOptions, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { ProtocolSchedule preMergeProtocolSchedule = - MainnetProtocolSchedule.fromConfig(genesisConfigOptions, miningParameters, badBlockManager); + MainnetProtocolSchedule.fromConfig( + genesisConfigOptions, + miningParameters, + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); ProtocolSchedule postMergeProtocolSchedule = MergeProtocolSchedule.create( - genesisConfigOptions, false, miningParameters, badBlockManager); + genesisConfigOptions, + false, + miningParameters, + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); return new TransitionProtocolSchedule( preMergeProtocolSchedule, postMergeProtocolSchedule, PostMergeContext.get()); } diff --git a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/MergeProtocolScheduleTest.java b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/MergeProtocolScheduleTest.java index b1f7414e45..0d4aede3a1 100644 --- a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/MergeProtocolScheduleTest.java +++ b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/MergeProtocolScheduleTest.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.evm.operation.InvalidOperation; import org.hyperledger.besu.evm.operation.PrevRanDaoOperation; import org.hyperledger.besu.evm.operation.Push0Operation; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; @@ -48,7 +49,12 @@ public class MergeProtocolScheduleTest { final GenesisConfigOptions config = GenesisConfigFile.fromConfig(jsonInput).getConfigOptions(); final ProtocolSchedule protocolSchedule = MergeProtocolSchedule.create( - config, false, MiningParameters.MINING_DISABLED, new BadBlockManager()); + config, + false, + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final ProtocolSpec homesteadSpec = protocolSchedule.getByBlockHeader(blockHeader(1)); final ProtocolSpec londonSpec = protocolSchedule.getByBlockHeader(blockHeader(1559)); @@ -64,7 +70,12 @@ public class MergeProtocolScheduleTest { final GenesisConfigOptions config = GenesisConfigFile.mainnet().getConfigOptions(); final ProtocolSchedule protocolSchedule = MergeProtocolSchedule.create( - config, false, MiningParameters.MINING_DISABLED, new BadBlockManager()); + config, + false, + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final long lastParisBlockNumber = 17034869L; final ProtocolSpec parisSpec = @@ -100,7 +111,12 @@ public class MergeProtocolScheduleTest { final GenesisConfigOptions config = GenesisConfigFile.fromConfig(jsonInput).getConfigOptions(); final ProtocolSchedule protocolSchedule = MergeProtocolSchedule.create( - config, false, MiningParameters.MINING_DISABLED, new BadBlockManager()); + config, + false, + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final ProtocolSpec parisSpec = protocolSchedule.getByBlockHeader( @@ -128,7 +144,12 @@ public class MergeProtocolScheduleTest { final GenesisConfigOptions config = GenesisConfigFile.mainnet().getConfigOptions(); final ProtocolSchedule protocolSchedule = MergeProtocolSchedule.create( - config, false, MiningParameters.MINING_DISABLED, new BadBlockManager()); + config, + false, + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final long lastParisBlockNumber = 17034869L; final ProtocolSpec parisSpec = @@ -160,7 +181,9 @@ public class MergeProtocolScheduleTest { GenesisConfigFile.DEFAULT.getConfigOptions(), false, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .getByBlockHeader(blockHeader(0)); assertThat(london.getName()).isEqualTo("Paris"); diff --git a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeGenesisConfigHelper.java b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeGenesisConfigHelper.java index 54099f868c..dcfe5f98e3 100644 --- a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeGenesisConfigHelper.java +++ b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeGenesisConfigHelper.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.chain.BadBlockManager; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.io.IOException; import java.net.URI; @@ -56,6 +57,8 @@ public interface MergeGenesisConfigHelper { getPosGenesisConfigFile().getConfigOptions(), false, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } } 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 5cf74f9458..3467dce9fb 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 @@ -437,7 +437,9 @@ public class TestContextBuilder { BFT_EXTRA_DATA_ENCODER, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final BftValidatorOverrides validatorOverrides = convertBftForks(qbftForks); final TransactionSimulator transactionSimulator = diff --git a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleBuilder.java b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleBuilder.java index 58a50efa26..44c7ddfba8 100644 --- a/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleBuilder.java +++ b/consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleBuilder.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.util.Optional; @@ -50,6 +51,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder * @param evmConfiguration the evm configuration * @param miningParameters The mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. + * @param metricsSystem A metricSystem instance to be able to expose metrics in the underlying + * calls * @return the protocol schedule */ public static BftProtocolSchedule create( @@ -60,7 +64,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder final BftExtraDataCodec bftExtraDataCodec, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return new QbftProtocolScheduleBuilder() .createProtocolSchedule( config, @@ -70,7 +76,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder bftExtraDataCodec, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } /** @@ -82,6 +90,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder * @param evmConfiguration the evm configuration * @param miningParameters The mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. + * @param metricsSystem A metricSystem instance to be able to expose metrics in the underlying + * calls * @return the protocol schedule */ public static BftProtocolSchedule create( @@ -90,7 +101,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder final BftExtraDataCodec bftExtraDataCodec, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return create( config, qbftForksSchedule, @@ -99,7 +112,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder bftExtraDataCodec, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } /** @@ -111,6 +126,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder * @param bftExtraDataCodec the bft extra data codec * @param miningParameters The mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. + * @param metricsSystem A metricSystem instance to be able to expose metrics in the underlying + * calls * @return the protocol schedule */ public static ProtocolSchedule create( @@ -119,7 +137,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder final boolean isRevertReasonEnabled, final BftExtraDataCodec bftExtraDataCodec, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return create( config, qbftForksSchedule, @@ -128,7 +148,9 @@ public class QbftProtocolScheduleBuilder extends BaseBftProtocolScheduleBuilder bftExtraDataCodec, EvmConfiguration.DEFAULT, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } @Override diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java index 07b3be34b4..020d6e0e5a 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java @@ -41,6 +41,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.Util; import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; import java.util.Collection; @@ -138,7 +139,9 @@ public class QbftProtocolScheduleTest { bftExtraDataCodec, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } private boolean validateHeader( diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerTest.java index 0aa1f7843b..71ea4131a3 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerTest.java @@ -78,6 +78,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.util.Subscribers; import java.math.BigInteger; @@ -184,7 +185,9 @@ public class QbftBlockHeightManagerTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); ProtocolSchedule protocolSchedule = new BftProtocolSchedule( diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java index 63449d74b5..ad4fe7ddb1 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java @@ -34,6 +34,13 @@ public class Blake2bfMessageDigest extends BCMessageDigest implements Cloneable super(new Blake2bfDigest()); } + @Override + public Blake2bfMessageDigest clone() throws CloneNotSupportedException { + Blake2bfMessageDigest cloned = (Blake2bfMessageDigest) super.clone(); + cloned.digest = ((Blake2bfDigest) this.digest).clone(); + return cloned; + } + /** * Implementation of the `F` compression function of the Blake2b cryptographic hash function. * @@ -43,7 +50,7 @@ public class Blake2bfMessageDigest extends BCMessageDigest implements Cloneable * *

Optimized for 64-bit platforms */ - public static class Blake2bfDigest implements Digest { + public static class Blake2bfDigest implements Digest, Cloneable { /** The constant MESSAGE_LENGTH_BYTES. */ public static final int MESSAGE_LENGTH_BYTES = 213; @@ -71,18 +78,18 @@ public class Blake2bfMessageDigest extends BCMessageDigest implements Cloneable // buffer which holds serialized input for this compression function // [ 4 bytes for rounds ][ 64 bytes for h ][ 128 bytes for m ] // [ 8 bytes for t_0 ][ 8 bytes for t_1 ][ 1 byte for f ] - private final byte[] buffer; + private byte[] buffer; private int bufferPos; // deserialized inputs for f compression - private final long[] h; - private final long[] m; - private final long[] t; + private long[] h; + private long[] m; + private long[] t; private boolean f; private long rounds; // unsigned integer represented as long - private final long[] v; + private long[] v; private static boolean useNative; static { @@ -112,6 +119,17 @@ public class Blake2bfMessageDigest extends BCMessageDigest implements Cloneable v = new long[16]; } + @Override + public Blake2bfDigest clone() throws CloneNotSupportedException { + Blake2bfDigest cloned = (Blake2bfDigest) super.clone(); + cloned.buffer = this.buffer.clone(); + cloned.h = this.h.clone(); + cloned.m = this.m.clone(); + cloned.t = this.t.clone(); + cloned.v = this.v.clone(); + return cloned; + } + /** Disable native. */ public static void disableNative() { useNative = false; diff --git a/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/Blake2bfMessageDigestTest.java b/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/Blake2bfMessageDigestTest.java index bd00dc7dae..390d7d4143 100644 --- a/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/Blake2bfMessageDigestTest.java +++ b/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/Blake2bfMessageDigestTest.java @@ -17,6 +17,13 @@ package org.hyperledger.besu.crypto; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.IntStream; + import org.bouncycastle.util.Pack; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,6 +36,16 @@ import org.junit.jupiter.params.provider.CsvFileSource; */ public class Blake2bfMessageDigestTest { + private static final SecureRandom SECURE_RANDOM; + + static { + try { + SECURE_RANDOM = SecureRandom.getInstanceStrong(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + private Blake2bfMessageDigest messageDigest; // output when input is all 0 @@ -124,6 +141,39 @@ public class Blake2bfMessageDigestTest { .isInstanceOf(IllegalArgumentException.class); } + @SuppressWarnings("unchecked") + @Test + public void testDigestThreadSafety() throws ExecutionException, InterruptedException { + final byte[] input = new byte[213]; + ; + SECURE_RANDOM.nextBytes(input); + int numberOfHashes = 10; + + CompletableFuture[] futures = + IntStream.range(0, numberOfHashes) + .mapToObj( + i -> + CompletableFuture.supplyAsync( + () -> { + try { + MessageDigest clonedDigest = messageDigest.clone(); + clonedDigest.update(input); + byte[] digest = clonedDigest.digest(); + return digest; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + })) + .toArray(CompletableFuture[]::new); + + CompletableFuture.allOf(futures).get(); + + byte[] expectedHash = futures[0].get(); + for (CompletableFuture future : futures) { + assertThat(expectedHash).isEqualTo(future.get()); + } + } + @ParameterizedTest @CsvFileSource(resources = "eip152TestCases.csv", numLinesToSkip = 1) public void eip152TestCases(final String hexIn, final String hexExpected) { diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/SetCodeAuthorization.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/SetCodeAuthorization.java new file mode 100644 index 0000000000..b12e65de41 --- /dev/null +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/SetCodeAuthorization.java @@ -0,0 +1,82 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.datatypes; + +import org.hyperledger.besu.crypto.SECPSignature; + +import java.math.BigInteger; +import java.util.Optional; + +/** + * SetCodeAuthorization is a data structure that represents the authorization to set code on a EOA + * account. + */ +public interface SetCodeAuthorization { + /** + * Return the chain id. + * + * @return chain id + */ + BigInteger chainId(); + + /** + * Return the address of the account which code will be used. + * + * @return address + */ + Address address(); + + /** + * Return the signature. + * + * @return signature + */ + SECPSignature signature(); + + /** + * Return the authorizer address. + * + * @return authorizer address of the EOA which will load the code into its account + */ + Optional

authorizer(); + + /** + * Return a valid nonce or empty otherwise. A nonce is valid if the size of the list is exactly 1 + * + * @return all the optional nonce + */ + Optional nonce(); + + /** + * Return the recovery id. + * + * @return byte + */ + byte v(); + + /** + * Return the r value of the signature. + * + * @return r value + */ + BigInteger r(); + + /** + * Return the s value of the signature. + * + * @return s value + */ + BigInteger s(); +} diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/StorageSlotKey.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/StorageSlotKey.java index a8bf7c9afd..8404453cf1 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/StorageSlotKey.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/StorageSlotKey.java @@ -94,7 +94,7 @@ public class StorageSlotKey implements Comparable { @Override public int hashCode() { - return Objects.hash(slotHash.hashCode()); + return slotHash.hashCode(); } @Override diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java index 4a77a898de..d6751852bc 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java @@ -234,4 +234,18 @@ public interface Transaction { * @return the size in bytes of the encoded transaction. */ int getSize(); + + /** + * Returns the set code transaction payload if this transaction is a 7702 transaction. + * + * @return the set code transaction payloads + */ + Optional> getAuthorizationList(); + + /** + * Returns the size of the authorization list. + * + * @return the size of the authorization list + */ + int authorizationListSize(); } diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java index 984a4cc746..df4a07193b 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/TransactionType.java @@ -27,10 +27,12 @@ public enum TransactionType { /** Eip1559 transaction type. */ EIP1559(0x02), /** Blob transaction type. */ - BLOB(0x03); + BLOB(0x03), + /** Eip7702 transaction type. */ + SET_CODE(0x04); private static final Set ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES = - Set.of(ACCESS_LIST, EIP1559, BLOB); + Set.of(ACCESS_LIST, EIP1559, BLOB, SET_CODE); private static final EnumSet LEGACY_FEE_MARKET_TRANSACTION_TYPES = EnumSet.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST); @@ -83,7 +85,8 @@ public enum TransactionType { TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559, - TransactionType.BLOB + TransactionType.BLOB, + TransactionType.SET_CODE }) .filter(transactionType -> transactionType.typeValue == serializedTypeValue) .findFirst() @@ -128,4 +131,13 @@ public enum TransactionType { public boolean supportsBlob() { return this.equals(BLOB); } + + /** + * Does transaction type require code. + * + * @return the boolean + */ + public boolean requiresSetCode() { + return this.equals(SET_CODE); + } } diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/BlockchainImporter.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/BlockchainImporter.java index 2a62331d33..3e261abf6d 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/BlockchainImporter.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/BlockchainImporter.java @@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; import org.hyperledger.besu.ethereum.util.RawBlockIterator; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.net.URL; import java.nio.file.Paths; @@ -46,7 +47,9 @@ public class BlockchainImporter { MainnetProtocolSchedule.fromConfig( GenesisConfigFile.fromConfig(genesisJson).getConfigOptions(), MiningParameters.newDefault(), - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final BlockHeaderFunctions blockHeaderFunctions = ScheduleBasedBlockHeaderFunctions.create(protocolSchedule); blocks = new ArrayList<>(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java index 60ba8990a1..b5aebf4f5d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java @@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory; public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated { - private final Optional cancun; + private final Optional supportedHardFork; private static final Logger LOG = LoggerFactory.getLogger(EngineForkchoiceUpdatedV3.class); public EngineForkchoiceUpdatedV3( @@ -43,7 +43,11 @@ public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated { final MergeMiningCoordinator mergeCoordinator, final EngineCallListener engineCallListener) { super(vertx, protocolSchedule, protocolContext, mergeCoordinator, engineCallListener); - this.cancun = protocolSchedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun")); + this.supportedHardFork = + protocolSchedule.hardforkFor( + s -> + s.fork().name().equalsIgnoreCase("Cancun") + || s.fork().name().equalsIgnoreCase("Prague")); } @Override @@ -77,12 +81,12 @@ public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated { @Override protected ValidationResult validateForkSupported(final long blockTimestamp) { if (protocolSchedule.isPresent()) { - if (cancun.isPresent() && blockTimestamp >= cancun.get().milestone()) { + if (supportedHardFork.isPresent() && blockTimestamp >= supportedHardFork.get().milestone()) { return ValidationResult.valid(); } else { return ValidationResult.invalid( RpcErrorType.UNSUPPORTED_FORK, - "Cancun configured to start at timestamp: " + cancun.get().milestone()); + "Cancun configured to start at timestamp: " + supportedHardFork.get().milestone()); } } else { return ValidationResult.invalid( @@ -99,7 +103,7 @@ public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated { return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadAttributesError())); } else if (payloadAttributes.getTimestamp().longValue() == 0) { return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadAttributesError())); - } else if (payloadAttributes.getTimestamp() < cancun.get().milestone()) { + } else if (payloadAttributes.getTimestamp() < supportedHardFork.get().milestone()) { return Optional.of(new JsonRpcErrorResponse(requestId, RpcErrorType.UNSUPPORTED_FORK)); } else { return Optional.empty(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EnginePayloadStatusResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EnginePayloadStatusResult.java index 4912f81e49..a9b9a17018 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EnginePayloadStatusResult.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EnginePayloadStatusResult.java @@ -19,7 +19,9 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngin import java.util.Optional; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; @JsonPropertyOrder({"status", "latestValidHash", "validationError"}) @@ -28,10 +30,11 @@ public class EnginePayloadStatusResult { Optional latestValidHash; Optional validationError; + @JsonCreator public EnginePayloadStatusResult( - final EngineStatus status, - final Hash latestValidHash, - final Optional validationError) { + @JsonProperty("status") final EngineStatus status, + @JsonProperty("latestValidHash") final Hash latestValidHash, + @JsonProperty("errorMessage") final Optional validationError) { this.status = status; this.latestValidHash = Optional.ofNullable(latestValidHash); this.validationError = validationError; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java index c8260d090c..05b323cacb 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results; import org.hyperledger.besu.datatypes.AccessListEntry; +import org.hyperledger.besu.datatypes.SetCodeAuthorization; import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; @@ -30,6 +31,7 @@ import org.apache.tuweni.bytes.Bytes; @JsonPropertyOrder({ "accessList", + "authorizationList", "blockHash", "blockNumber", "chainId", @@ -91,6 +93,9 @@ public class TransactionCompleteResult implements TransactionResult { @JsonInclude(JsonInclude.Include.NON_NULL) private final List versionedHashes; + @JsonInclude(JsonInclude.Include.NON_NULL) + private final List authorizationList; + public TransactionCompleteResult(final TransactionWithMetadata tx) { final Transaction transaction = tx.getTransaction(); final TransactionType transactionType = transaction.getType(); @@ -125,7 +130,8 @@ public class TransactionCompleteResult implements TransactionResult { this.yParity = Quantity.create(transaction.getYParity()); this.v = (transactionType == TransactionType.ACCESS_LIST - || transactionType == TransactionType.EIP1559) + || transactionType == TransactionType.EIP1559) + || transactionType == TransactionType.SET_CODE ? Quantity.create(transaction.getYParity()) : null; } @@ -133,6 +139,7 @@ public class TransactionCompleteResult implements TransactionResult { this.r = Quantity.create(transaction.getR()); this.s = Quantity.create(transaction.getS()); this.versionedHashes = transaction.getVersionedHashes().orElse(null); + this.authorizationList = transaction.getAuthorizationList().orElse(null); } @JsonGetter(value = "accessList") @@ -246,4 +253,9 @@ public class TransactionCompleteResult implements TransactionResult { public List getVersionedHashes() { return versionedHashes; } + + @JsonGetter(value = "authorizationList") + public List getAuthorizationList() { + return authorizationList; + } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostAllowlistTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostAllowlistTest.java index af5228dc06..80626ef2fc 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostAllowlistTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostAllowlistTest.java @@ -109,7 +109,9 @@ public class JsonRpcHttpServiceHostAllowlistTest { MainnetProtocolSchedule.fromConfig( new StubGenesisConfigOptions().constantinopleBlock(0).chainId(CHAIN_ID), MiningParameters.MINING_DISABLED, - new BadBlockManager()), + new BadBlockManager(), + false, + new NoOpMetricsSystem()), mock(ProtocolContext.class), mock(FilterManager.class), mock(TransactionPool.class), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java index 6ae5d37a0f..0fb55fdf34 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java @@ -138,7 +138,11 @@ public class JsonRpcHttpServiceLoginTest { blockchainQueries, synchronizer, MainnetProtocolSchedule.fromConfig( - genesisConfigOptions, MiningParameters.MINING_DISABLED, new BadBlockManager()), + genesisConfigOptions, + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()), mock(ProtocolContext.class), mock(FilterManager.class), mock(TransactionPool.class), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTestBase.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTestBase.java index f8c9dbc18f..a2a856333e 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTestBase.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTestBase.java @@ -118,7 +118,9 @@ public class JsonRpcHttpServiceTestBase { new StubGenesisConfigOptions().constantinopleBlock(0).chainId(CHAIN_ID), EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()), + new BadBlockManager(), + false, + new NoOpMetricsSystem()), mock(ProtocolContext.class), mock(FilterManager.class), mock(TransactionPool.class), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java index 2f62e490be..1d3a3a087a 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java @@ -123,7 +123,9 @@ public class JsonRpcHttpServiceTlsClientAuthTest { MainnetProtocolSchedule.fromConfig( new StubGenesisConfigOptions().constantinopleBlock(0).chainId(CHAIN_ID), MiningParameters.MINING_DISABLED, - new BadBlockManager()), + new BadBlockManager(), + false, + new NoOpMetricsSystem()), mock(ProtocolContext.class), mock(FilterManager.class), mock(TransactionPool.class), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java index 2ff0833bc6..684f843d2f 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java @@ -111,7 +111,9 @@ class JsonRpcHttpServiceTlsMisconfigurationTest { MainnetProtocolSchedule.fromConfig( new StubGenesisConfigOptions().constantinopleBlock(0).chainId(CHAIN_ID), MiningParameters.MINING_DISABLED, - new BadBlockManager()), + new BadBlockManager(), + false, + new NoOpMetricsSystem()), mock(ProtocolContext.class), mock(FilterManager.class), mock(TransactionPool.class), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java index e650f9d490..b6d7fa67f8 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java @@ -112,7 +112,9 @@ public class JsonRpcHttpServiceTlsTest { MainnetProtocolSchedule.fromConfig( new StubGenesisConfigOptions().constantinopleBlock(0).chainId(CHAIN_ID), MiningParameters.MINING_DISABLED, - new BadBlockManager()), + new BadBlockManager(), + false, + new NoOpMetricsSystem()), mock(ProtocolContext.class), mock(FilterManager.class), mock(TransactionPool.class), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java index 5fa137e312..75927c4513 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java @@ -176,7 +176,9 @@ public class WebSocketServiceLoginTest { MainnetProtocolSchedule.fromConfig( genesisConfigOptions, MiningParameters.MINING_DISABLED, - new BadBlockManager()), + new BadBlockManager(), + false, + new NoOpMetricsSystem()), mock(ProtocolContext.class), mock(FilterManager.class), mock(TransactionPool.class), diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java index c106b7aff1..ed028767d6 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.INVALID_TX_EVALUATION_TOO_LONG; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_EVALUATION_TOO_LONG; @@ -419,11 +420,14 @@ public class BlockTransactionSelector { final var pendingTransaction = evaluationContext.getPendingTransaction(); - // check if this tx took too much to evaluate, and in case remove it from the pool + // check if this tx took too much to evaluate, and in case it was invalid remove it from the + // pool, otherwise penalize it. final TransactionSelectionResult actualResult = isTimeout.get() - ? transactionTookTooLong(evaluationContext) - ? TX_EVALUATION_TOO_LONG + ? transactionTookTooLong(evaluationContext, selectionResult) + ? selectionResult.discard() + ? INVALID_TX_EVALUATION_TOO_LONG + : TX_EVALUATION_TOO_LONG : BLOCK_SELECTION_TIMEOUT : selectionResult; @@ -441,16 +445,21 @@ public class BlockTransactionSelector { return actualResult; } - private boolean transactionTookTooLong(final TransactionEvaluationContext evaluationContext) { + private boolean transactionTookTooLong( + final TransactionEvaluationContext evaluationContext, + final TransactionSelectionResult selectionResult) { final var evaluationTimer = evaluationContext.getEvaluationTimer(); if (evaluationTimer.elapsed(TimeUnit.MILLISECONDS) > blockTxsSelectionMaxTime) { LOG.atWarn() .setMessage( - "Transaction {} is too late for inclusion, evaluated in {} that is over the max limit of {}ms" - + ", removing it from the pool") + "Transaction {} is too late for inclusion, with result {}, evaluated in {} that is over the max limit of {}ms" + + ", {}") .addArgument(evaluationContext.getPendingTransaction()::getHash) + .addArgument(selectionResult) .addArgument(evaluationTimer) .addArgument(blockTxsSelectionMaxTime) + .addArgument( + selectionResult.discard() ? "removing it from the pool" : "penalizing it in the pool") .log(); return true; } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelector.java index 9df006722e..9a4c83e962 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelector.java @@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory; /** * This class extends AbstractTransactionSelector and provides a specific implementation for * evaluating transactions based on blobs size. It checks if a transaction supports blobs, and if - * so, checks that there is enough remaining data gas in the block to fit the blobs of the tx. + * so, checks that there is enough remaining blob gas in the block to fit the blobs of the tx. */ public class BlobSizeTransactionSelector extends AbstractTransactionSelector { private static final Logger LOG = LoggerFactory.getLogger(BlobSizeTransactionSelector.class); diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java index 85d97bdf46..9eed2bff09 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java @@ -110,7 +110,8 @@ public class ProcessingResultTransactionSelector extends AbstractTransactionSele * @return True if the invalid reason is transient, false otherwise. */ private boolean isTransientValidationError(final TransactionInvalidReason invalidReason) { - return invalidReason.equals(TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE) + return invalidReason.equals(TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE) + || invalidReason.equals(TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE) || invalidReason.equals(TransactionInvalidReason.NONCE_TOO_HIGH); } } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java index 2444d9db7d..946d4ab09d 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java @@ -395,7 +395,9 @@ abstract class AbstractBlockCreatorTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule()) .build(); diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java index d70589ae83..adcc3ee1e2 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java @@ -18,8 +18,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.awaitility.Awaitility.await; import static org.hyperledger.besu.ethereum.core.MiningParameters.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME; -import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE; +import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.NONCE_TOO_LOW; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.INVALID_TX_EVALUATION_TOO_LONG; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_EVALUATION_TOO_LONG; @@ -223,7 +224,9 @@ public abstract class AbstractBlockTransactionSelectorTest { GenesisConfigFile.fromResource("/dev.json").getConfigOptions(), EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final MainnetTransactionProcessor mainnetTransactionProcessor = protocolSchedule.getByBlockHeader(blockHeader(0)).getTransactionProcessor(); @@ -294,7 +297,7 @@ public abstract class AbstractBlockTransactionSelectorTest { final Transaction tx = createTransaction(i, Wei.of(7), 100_000); transactionsToInject.add(tx); if (i == 1) { - ensureTransactionIsInvalid(tx, TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE); + ensureTransactionIsInvalid(tx, TransactionInvalidReason.NONCE_TOO_LOW); } else { ensureTransactionIsValid(tx); } @@ -309,8 +312,7 @@ public abstract class AbstractBlockTransactionSelectorTest { .containsOnly( entry( invalidTx, - TransactionSelectionResult.invalid( - TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE.name()))); + TransactionSelectionResult.invalid(TransactionInvalidReason.NONCE_TOO_LOW.name()))); assertThat(results.getSelectedTransactions().size()).isEqualTo(4); assertThat(results.getSelectedTransactions().contains(invalidTx)).isFalse(); assertThat(results.getReceipts().size()).isEqualTo(4); @@ -566,8 +568,7 @@ public abstract class AbstractBlockTransactionSelectorTest { ensureTransactionIsValid(validTransaction, 21_000, 0); final Transaction invalidTransaction = createTransaction(3, Wei.of(10), 21_000); - ensureTransactionIsInvalid( - invalidTransaction, TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE); + ensureTransactionIsInvalid(invalidTransaction, TransactionInvalidReason.NONCE_TOO_LOW); transactionPool.addRemoteTransactions(List.of(validTransaction, invalidTransaction)); @@ -580,8 +581,7 @@ public abstract class AbstractBlockTransactionSelectorTest { .containsOnly( entry( invalidTransaction, - TransactionSelectionResult.invalid( - TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE.name()))); + TransactionSelectionResult.invalid(TransactionInvalidReason.NONCE_TOO_LOW.name()))); } @Test @@ -946,7 +946,7 @@ public abstract class AbstractBlockTransactionSelectorTest { @ParameterizedTest @MethodSource("subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver") - public void pendingTransactionsThatTakesTooLongToEvaluateIsDroppedFromThePool( + public void pendingTransactionsThatTakesTooLongToEvaluateIsPenalized( final boolean isPoa, final boolean preProcessingTooLate, final boolean processingTooLate, @@ -959,7 +959,7 @@ public abstract class AbstractBlockTransactionSelectorTest { postProcessingTooLate, 900, TX_EVALUATION_TOO_LONG, - true); + false); } private void internalBlockSelectionTimeoutSimulation( @@ -1083,7 +1083,7 @@ public abstract class AbstractBlockTransactionSelectorTest { 500, BLOCK_SELECTION_TIMEOUT, false, - UPFRONT_COST_EXCEEDS_BALANCE); + NONCE_TOO_LOW); } @ParameterizedTest @@ -1100,9 +1100,9 @@ public abstract class AbstractBlockTransactionSelectorTest { processingTooLate, postProcessingTooLate, 900, - TX_EVALUATION_TOO_LONG, + INVALID_TX_EVALUATION_TOO_LONG, true, - UPFRONT_COST_EXCEEDS_BALANCE); + NONCE_TOO_LOW); } private void internalBlockSelectionTimeoutSimulationInvalidTxs( @@ -1421,15 +1421,17 @@ public abstract class AbstractBlockTransactionSelectorTest { private static class PluginTransactionSelectionResult extends TransactionSelectionResult { private enum PluginStatus implements Status { - PLUGIN_INVALID(false, true), - PLUGIN_INVALID_TRANSIENT(false, false); + PLUGIN_INVALID(false, true, false), + PLUGIN_INVALID_TRANSIENT(false, false, true); private final boolean stop; private final boolean discard; + private final boolean penalize; - PluginStatus(final boolean stop, final boolean discard) { + PluginStatus(final boolean stop, final boolean discard, final boolean penalize) { this.stop = stop; this.discard = discard; + this.penalize = penalize; } @Override @@ -1441,6 +1443,11 @@ public abstract class AbstractBlockTransactionSelectorTest { public boolean discard() { return discard; } + + @Override + public boolean penalize() { + return penalize; + } } public static final TransactionSelectionResult GENERIC_PLUGIN_INVALID_TRANSIENT = diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java index 6206384534..416e5fd234 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java @@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.util.number.Fraction; @@ -60,7 +61,9 @@ public class LegacyFeeMarketBlockTransactionSelectorTest false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java index e9bfcfb762..a59841d4f4 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java @@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.testutil.TestClock; import org.hyperledger.besu.util.number.Fraction; @@ -71,7 +72,9 @@ public class LondonFeeMarketBlockTransactionSelectorTest false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java index 2cd5bc5e9e..7e3c01200c 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java @@ -97,8 +97,10 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest { PrivacyParameters.DEFAULT, false, EvmConfiguration.DEFAULT, - miningParameters, - new BadBlockManager()) + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule()) .build(); @@ -158,8 +160,10 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest { PrivacyParameters.DEFAULT, false, EvmConfiguration.DEFAULT, - miningParameters, - new BadBlockManager()) + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule()) .build(); @@ -209,8 +213,10 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest { PrivacyParameters.DEFAULT, false, EvmConfiguration.DEFAULT, - miningParameters, - new BadBlockManager()) + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); final ExecutionContextTestFixture executionContextTestFixture = ExecutionContextTestFixture.builder(genesisConfigFile) @@ -285,8 +291,10 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest { PrivacyParameters.DEFAULT, false, EvmConfiguration.DEFAULT, - miningParameters, - new BadBlockManager()) + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); final ExecutionContextTestFixture executionContextTestFixture = ExecutionContextTestFixture.builder(genesisConfigFile) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainDataPruner.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainDataPruner.java index 11a1500e4e..37025feca7 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainDataPruner.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainDataPruner.java @@ -76,7 +76,7 @@ public class ChainDataPruner implements BlockAddedObserver { if (event.isNewCanonicalHead() && blocksToBePruned >= pruningFrequency) { long currentRetainedBlock = blockNumber - currentPruningMark + 1; while (currentRetainedBlock > blocksToRetain) { - LOG.debug("Pruning chain data with block height of " + currentPruningMark); + LOG.debug("Pruning chain data with block height of {}", currentPruningMark); pruneChainDataAtBlock(pruningTransaction, currentPruningMark); currentPruningMark++; currentRetainedBlock = blockNumber - currentPruningMark; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/SetCodeAuthorization.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/SetCodeAuthorization.java new file mode 100644 index 0000000000..ea9cae2fcb --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/SetCodeAuthorization.java @@ -0,0 +1,260 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core; + +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.encoding.SetCodeTransactionEncoder; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; + +import java.math.BigInteger; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Suppliers; +import org.apache.tuweni.bytes.Bytes; + +public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetCodeAuthorization { + private static final Supplier SIGNATURE_ALGORITHM = + Suppliers.memoize(SignatureAlgorithmFactory::getInstance); + + public static final Bytes MAGIC = Bytes.fromHexString("05"); + + private final BigInteger chainId; + private final Address address; + private final Optional nonce; + private final SECPSignature signature; + private Optional
authorizer = Optional.empty(); + private boolean isAuthorityComputed = false; + + /** + * An access list entry as defined in EIP-7702 + * + * @param chainId can be either the current chain id or zero + * @param address the address from which the code will be set into the EOA account + * @param nonce an optional nonce after which this auth expires + * @param signature the signature of the EOA account which will be used to set the code + */ + public SetCodeAuthorization( + final BigInteger chainId, + final Address address, + final Optional nonce, + final SECPSignature signature) { + this.chainId = chainId; + this.address = address; + this.nonce = nonce; + this.signature = signature; + } + + /** + * Create access list entry. + * + * @param chainId can be either the current chain id or zero + * @param address the address from which the code will be set into the EOA account + * @param nonces the list of nonces + * @param v the recovery id + * @param r the r value of the signature + * @param s the s value of the signature + * @return SetCodeTransactionEntry + */ + @JsonCreator + public static org.hyperledger.besu.datatypes.SetCodeAuthorization createSetCodeAuthorizationEntry( + @JsonProperty("chainId") final BigInteger chainId, + @JsonProperty("address") final Address address, + @JsonProperty("nonce") final List nonces, + @JsonProperty("v") final byte v, + @JsonProperty("r") final BigInteger r, + @JsonProperty("s") final BigInteger s) { + return new SetCodeAuthorization( + chainId, + address, + Optional.ofNullable(nonces.get(0)), + SIGNATURE_ALGORITHM.get().createSignature(r, s, v)); + } + + @JsonProperty("chainId") + @Override + public BigInteger chainId() { + return chainId; + } + + @JsonProperty("address") + @Override + public Address address() { + return address; + } + + @JsonProperty("signature") + @Override + public SECPSignature signature() { + return signature; + } + + @Override + public Optional
authorizer() { + if (!isAuthorityComputed) { + authorizer = computeAuthority(); + isAuthorityComputed = true; + } + + return authorizer; + } + + @Override + public Optional nonce() { + return nonce; + } + + @JsonProperty("v") + @Override + public byte v() { + return signature.getRecId(); + } + + @JsonProperty("r") + @Override + public BigInteger r() { + return signature.getR(); + } + + @JsonProperty("s") + @Override + public BigInteger s() { + return signature.getS(); + } + + private Optional
computeAuthority() { + BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); + SetCodeTransactionEncoder.encodeSingleSetCodeWithoutSignature(this, rlpOutput); + + final Hash hash = Hash.hash(Bytes.concatenate(MAGIC, rlpOutput.encoded())); + + return SIGNATURE_ALGORITHM + .get() + .recoverPublicKeyFromSignature(hash, signature) + .map(Address::extract); + } + + /** + * Create set code authorization with a builder. + * + * @return SetCodeAuthorization.Builder + */ + public static Builder builder() { + return new Builder(); + } + + /** Builder for SetCodeAuthorization. */ + public static class Builder { + private BigInteger chainId = BigInteger.ZERO; + private Address address; + private Optional nonce = Optional.empty(); + private SECPSignature signature; + + /** Create a new builder. */ + protected Builder() {} + + /** + * Set the optional chain id. + * + * @param chainId the chain id + * @return this builder + */ + public Builder chainId(final BigInteger chainId) { + this.chainId = chainId; + return this; + } + + /** + * Set the address of the authorized smart contract. + * + * @param address the address + * @return this builder + */ + public Builder address(final Address address) { + this.address = address; + return this; + } + + /** + * Set the optional nonce. + * + * @param nonce the optional nonce. + * @return this builder + */ + public Builder nonces(final Optional nonce) { + this.nonce = nonce; + return this; + } + + /** + * Set the signature of the authorizer account. + * + * @param signature the signature + * @return this builder + */ + public Builder signature(final SECPSignature signature) { + this.signature = signature; + return this; + } + + /** + * Sign the authorization with the given key pair and return the authorization. + * + * @param keyPair the key pair + * @return SetCodeAuthorization + */ + public org.hyperledger.besu.datatypes.SetCodeAuthorization signAndBuild(final KeyPair keyPair) { + final BytesValueRLPOutput output = new BytesValueRLPOutput(); + output.startList(); + output.writeBigIntegerScalar(chainId); + output.writeBytes(address); + output.startList(); + nonce.ifPresent(output::writeLongScalar); + output.endList(); + output.endList(); + + signature( + SIGNATURE_ALGORITHM + .get() + .sign(Hash.hash(Bytes.concatenate(MAGIC, output.encoded())), keyPair)); + return build(); + } + + /** + * Build the authorization. + * + * @return SetCodeAuthorization + */ + public org.hyperledger.besu.datatypes.SetCodeAuthorization build() { + if (address == null) { + throw new IllegalStateException("Address must be set"); + } + + if (signature == null) { + throw new IllegalStateException("Signature must be set"); + } + + return new SetCodeAuthorization(chainId, address, nonce, signature); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index 7ca349721f..513cfb8319 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.datatypes.BlobsWithCommitments; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.KZGCommitment; import org.hyperledger.besu.datatypes.KZGProof; +import org.hyperledger.besu.datatypes.SetCodeAuthorization; import org.hyperledger.besu.datatypes.Sha256Hash; import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.VersionedHash; @@ -38,6 +39,7 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.encoding.AccessListTransactionEncoder; import org.hyperledger.besu.ethereum.core.encoding.BlobTransactionEncoder; import org.hyperledger.besu.ethereum.core.encoding.EncodingContext; +import org.hyperledger.besu.ethereum.core.encoding.SetCodeTransactionEncoder; import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder; import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; @@ -122,6 +124,7 @@ public class Transaction private final Optional> versionedHashes; private final Optional blobsWithCommitments; + private final Optional> maybeAuthorizationList; public static Builder builder() { return new Builder(); @@ -177,7 +180,8 @@ public class Transaction final Address sender, final Optional chainId, final Optional> versionedHashes, - final Optional blobsWithCommitments) { + final Optional blobsWithCommitments, + final Optional> maybeAuthorizationList) { if (!forCopy) { if (transactionType.requiresChainId()) { @@ -213,6 +217,12 @@ public class Transaction checkArgument( maxFeePerBlobGas.isPresent(), "Must specify max fee per blob gas for blob transaction"); } + + if (transactionType.requiresSetCode()) { + checkArgument( + maybeAuthorizationList.isPresent(), + "Must specify set code transaction payload for set code transaction"); + } } this.transactionType = transactionType; @@ -231,6 +241,7 @@ public class Transaction this.chainId = chainId; this.versionedHashes = versionedHashes; this.blobsWithCommitments = blobsWithCommitments; + this.maybeAuthorizationList = maybeAuthorizationList; } /** @@ -462,6 +473,7 @@ public class Transaction payload, maybeAccessList, versionedHashes.orElse(null), + maybeAuthorizationList, chainId); } return hashNoSignature; @@ -668,6 +680,16 @@ public class Transaction return blobsWithCommitments; } + @Override + public Optional> getAuthorizationList() { + return maybeAuthorizationList; + } + + @Override + public int authorizationListSize() { + return maybeAuthorizationList.map(List::size).orElse(0); + } + /** * Return the list of transaction hashes extracted from the collection of Transaction passed as * argument @@ -692,6 +714,7 @@ public class Transaction final Bytes payload, final Optional> accessList, final List versionedHashes, + final Optional> authorizationList, final Optional chainId) { if (transactionType.requiresChainId()) { checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType); @@ -736,6 +759,21 @@ public class Transaction new IllegalStateException( "Developer error: the transaction should be guaranteed to have an access list here")), chainId); + case SET_CODE -> + setCodePreimage( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + authorizationList.orElseThrow( + () -> + new IllegalStateException( + "Developer error: the transaction should be guaranteed to have a set code payload here"))); }; return keccak256(preimage); } @@ -873,6 +911,38 @@ public class Transaction return Bytes.concatenate(Bytes.of(TransactionType.ACCESS_LIST.getSerializedType()), encode); } + private static Bytes setCodePreimage( + final long nonce, + final Wei maxPriorityFeePerGas, + final Wei maxFeePerGas, + final long gasLimit, + final Optional
to, + final Wei value, + final Bytes payload, + final Optional chainId, + final Optional> accessList, + final List authorizationList) { + final Bytes encoded = + RLP.encode( + rlpOutput -> { + rlpOutput.startList(); + eip1559PreimageFields( + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + to, + value, + payload, + chainId, + accessList, + rlpOutput); + SetCodeTransactionEncoder.encodeSetCodeInner(authorizationList, rlpOutput); + rlpOutput.endList(); + }); + return Bytes.concatenate(Bytes.of(TransactionType.SET_CODE.getSerializedType()), encoded); + } + @Override public boolean equals(final Object other) { if (!(other instanceof Transaction that)) { @@ -1040,7 +1110,8 @@ public class Transaction sender, chainId, detachedVersionedHashes, - detachedBlobsWithCommitments); + detachedBlobsWithCommitments, + maybeAuthorizationList); // copy also the computed fields, to avoid to recompute them copiedTx.sender = this.sender; @@ -1108,6 +1179,7 @@ public class Transaction protected Optional v = Optional.empty(); protected List versionedHashes = null; private BlobsWithCommitments blobsWithCommitments; + protected Optional> setCodeTransactionPayloads = Optional.empty(); public Builder copiedFrom(final Transaction toCopy) { this.transactionType = toCopy.transactionType; @@ -1126,6 +1198,7 @@ public class Transaction this.chainId = toCopy.chainId; this.versionedHashes = toCopy.versionedHashes.orElse(null); this.blobsWithCommitments = toCopy.blobsWithCommitments.orElse(null); + this.setCodeTransactionPayloads = toCopy.maybeAuthorizationList; return this; } @@ -1219,6 +1292,8 @@ public class Transaction transactionType = TransactionType.EIP1559; } else if (accessList.isPresent()) { transactionType = TransactionType.ACCESS_LIST; + } else if (setCodeTransactionPayloads.isPresent()) { + transactionType = TransactionType.SET_CODE; } else { transactionType = TransactionType.FRONTIER; } @@ -1248,7 +1323,8 @@ public class Transaction sender, chainId, Optional.ofNullable(versionedHashes), - Optional.ofNullable(blobsWithCommitments)); + Optional.ofNullable(blobsWithCommitments), + setCodeTransactionPayloads); } public Transaction signAndBuild(final KeyPair keys) { @@ -1275,6 +1351,7 @@ public class Transaction payload, accessList, versionedHashes, + setCodeTransactionPayloads, chainId), keys); } @@ -1298,5 +1375,11 @@ public class Transaction this.blobsWithCommitments = blobsWithCommitments; return this; } + + public Builder setCodeTransactionPayloads( + final List setCodeTransactionEntries) { + this.setCodeTransactionPayloads = Optional.ofNullable(setCodeTransactionEntries); + return this; + } } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoder.java new file mode 100644 index 0000000000..80d59de83b --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoder.java @@ -0,0 +1,116 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core.encoding; + +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.AccessListEntry; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.TransactionType; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.SetCodeAuthorization; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.rlp.RLPInput; + +import java.math.BigInteger; +import java.util.Optional; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; + +public class SetCodeTransactionDecoder { + private static final Supplier SIGNATURE_ALGORITHM = + Suppliers.memoize(SignatureAlgorithmFactory::getInstance); + + private SetCodeTransactionDecoder() { + // private constructor + } + + public static Transaction decode(final RLPInput input) { + input.enterList(); + final BigInteger chainId = input.readBigIntegerScalar(); + final Transaction.Builder builder = + Transaction.builder() + .type(TransactionType.SET_CODE) + .chainId(chainId) + .nonce(input.readLongScalar()) + .maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar())) + .maxFeePerGas(Wei.of(input.readUInt256Scalar())) + .gasLimit(input.readLongScalar()) + .to(input.readBytes(v -> v.isEmpty() ? null : Address.wrap(v))) + .value(Wei.of(input.readUInt256Scalar())) + .payload(input.readBytes()) + .accessList( + input.readList( + accessListEntryRLPInput -> { + accessListEntryRLPInput.enterList(); + final AccessListEntry accessListEntry = + new AccessListEntry( + Address.wrap(accessListEntryRLPInput.readBytes()), + accessListEntryRLPInput.readList(RLPInput::readBytes32)); + accessListEntryRLPInput.leaveList(); + return accessListEntry; + })) + .setCodeTransactionPayloads( + input.readList( + setCodeTransactionPayloadsRLPInput -> + decodeInnerPayload(setCodeTransactionPayloadsRLPInput))); + + final byte recId = (byte) input.readUnsignedByteScalar(); + final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger(); + final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger(); + + input.leaveList(); + + final Transaction transaction = + builder.signature(SIGNATURE_ALGORITHM.get().createSignature(r, s, recId)).build(); + + return transaction; + } + + public static org.hyperledger.besu.datatypes.SetCodeAuthorization decodeInnerPayload( + final RLPInput input) { + input.enterList(); + final BigInteger chainId = input.readBigIntegerScalar(); + final Address address = Address.wrap(input.readBytes()); + + Optional nonce = Optional.empty(); + + if (!input.nextIsList()) { + throw new IllegalArgumentException("Optional nonce must be an list, but isn't"); + } + + final long noncesSize = input.nextSize(); + + input.enterList(); + if (noncesSize == 1) { + nonce = Optional.ofNullable(input.readLongScalar()); + } else if (noncesSize > 1) { + throw new IllegalArgumentException("Nonce list may only have 1 member, if any"); + } + input.leaveList(); + + final byte yParity = (byte) input.readUnsignedByteScalar(); + final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger(); + final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger(); + + input.leaveList(); + + final SECPSignature signature = SIGNATURE_ALGORITHM.get().createSignature(r, s, yParity); + + return new SetCodeAuthorization(chainId, address, nonce, signature); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoder.java new file mode 100644 index 0000000000..05808f3129 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoder.java @@ -0,0 +1,89 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core.encoding; + +import static org.hyperledger.besu.ethereum.core.encoding.AccessListTransactionEncoder.writeAccessList; +import static org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder.writeSignatureAndRecoveryId; + +import org.hyperledger.besu.datatypes.SetCodeAuthorization; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.util.List; + +import org.apache.tuweni.bytes.Bytes; + +public class SetCodeTransactionEncoder { + + private SetCodeTransactionEncoder() { + // private constructor + } + + public static void encodeSetCodeInner( + final List payloads, final RLPOutput rlpOutput) { + rlpOutput.startList(); + payloads.forEach(payload -> encodeSingleSetCode(payload, rlpOutput)); + rlpOutput.endList(); + } + + public static void encodeSingleSetCodeWithoutSignature( + final SetCodeAuthorization payload, final RLPOutput rlpOutput) { + rlpOutput.startList(); + encodeAuthorizationDetails(payload, rlpOutput); + rlpOutput.endList(); + } + + public static void encodeSingleSetCode( + final SetCodeAuthorization payload, final RLPOutput rlpOutput) { + rlpOutput.startList(); + encodeAuthorizationDetails(payload, rlpOutput); + rlpOutput.writeIntScalar(payload.signature().getRecId()); + rlpOutput.writeBigIntegerScalar(payload.signature().getR()); + rlpOutput.writeBigIntegerScalar(payload.signature().getS()); + rlpOutput.endList(); + } + + private static void encodeAuthorizationDetails( + final SetCodeAuthorization payload, final RLPOutput rlpOutput) { + rlpOutput.writeBigIntegerScalar(payload.chainId()); + rlpOutput.writeBytes(payload.address().copy()); + rlpOutput.startList(); + payload.nonce().ifPresent(rlpOutput::writeLongScalar); + rlpOutput.endList(); + } + + public static void encode(final Transaction transaction, final RLPOutput out) { + out.startList(); + out.writeBigIntegerScalar(transaction.getChainId().orElseThrow()); + out.writeLongScalar(transaction.getNonce()); + out.writeUInt256Scalar(transaction.getMaxPriorityFeePerGas().orElseThrow()); + out.writeUInt256Scalar(transaction.getMaxFeePerGas().orElseThrow()); + out.writeLongScalar(transaction.getGasLimit()); + out.writeBytes(transaction.getTo().map(Bytes::copy).orElse(Bytes.EMPTY)); + out.writeUInt256Scalar(transaction.getValue()); + out.writeBytes(transaction.getPayload()); + writeAccessList(out, transaction.getAccessList()); + encodeSetCodeInner( + transaction + .getAuthorizationList() + .orElseThrow( + () -> + new IllegalStateException( + "Developer error: the transaction should be guaranteed to have a set code payload here")), + out); + writeSignatureAndRecoveryId(transaction, out); + out.endList(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java index fe6687cdb5..2a63d8f24b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java @@ -40,7 +40,9 @@ public class TransactionDecoder { TransactionType.EIP1559, EIP1559TransactionDecoder::decode, TransactionType.BLOB, - BlobTransactionDecoder::decode); + BlobTransactionDecoder::decode, + TransactionType.SET_CODE, + SetCodeTransactionDecoder::decode); private static final ImmutableMap POOLED_TRANSACTION_DECODERS = ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionDecoder::decode); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java index 20fe75885e..4995975621 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java @@ -39,7 +39,9 @@ public class TransactionEncoder { TransactionType.EIP1559, EIP1559TransactionEncoder::encode, TransactionType.BLOB, - BlobTransactionEncoder::encode); + BlobTransactionEncoder::encode, + TransactionType.SET_CODE, + SetCodeTransactionEncoder::encode); private static final ImmutableMap POOLED_TRANSACTION_ENCODERS = ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionEncoder::encode); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java index b7f8395d92..b86b2b0de2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java @@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; /** A ProtocolSchedule which behaves similarly to MainNet, but with a much reduced difficulty. */ public class FixedDifficultyProtocolSchedule { @@ -32,7 +33,9 @@ public class FixedDifficultyProtocolSchedule { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return new ProtocolScheduleBuilder( config, ProtocolSpecAdapters.create( @@ -43,7 +46,9 @@ public class FixedDifficultyProtocolSchedule { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager) + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem) .createProtocolSchedule(); } @@ -52,27 +57,35 @@ public class FixedDifficultyProtocolSchedule { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return create( config, PrivacyParameters.DEFAULT, isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } public static ProtocolSchedule create( final GenesisConfigOptions config, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return create( config, PrivacyParameters.DEFAULT, false, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkIdManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkIdManager.java index 859fb918a4..407e5149d6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkIdManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/forkid/ForkIdManager.java @@ -79,8 +79,7 @@ public class ForkIdManager { .sorted() .collect(Collectors.toUnmodifiableList()); final List allForkNumbers = - Stream.concat(blockNumberForks.stream(), timestampForks.stream()) - .collect(Collectors.toList()); + Stream.concat(blockNumberForks.stream(), timestampForks.stream()).toList(); this.forkNext = createForkIds(); this.allForkIds = Stream.concat(blockNumbersForkIds.stream(), timestampsForkIds.stream()) @@ -93,7 +92,7 @@ public class ForkIdManager { public ForkId getForkIdForChainHead() { if (legacyEth64) { return blockNumbersForkIds.isEmpty() - ? null + ? new ForkId(genesisHashCrc, 0) : blockNumbersForkIds.get(blockNumbersForkIds.size() - 1); } final BlockHeader header = chainHeadSupplier.get(); 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 8ecfa453d9..480a1d8d7a 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 @@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorld 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; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -110,57 +111,67 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { protocolSpec.getBlockHashProcessor().processBlockHashes(blockchain, worldState, blockHeader); final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain); - for (final Transaction transaction : transactions) { + final Address miningBeneficiary = miningBeneficiaryCalculator.calculateBeneficiary(blockHeader); + + Optional maybeParentHeader = + blockchain.getBlockHeader(blockHeader.getParentHash()); + + Wei blobGasPrice = + maybeParentHeader + .map( + parentHeader -> + protocolSpec + .getFeeMarket() + .blobGasPricePerGas( + calculateExcessBlobGasForParent(protocolSpec, parentHeader))) + .orElse(Wei.ZERO); + + final Optional preProcessingContext = + runBlockPreProcessing( + worldState, + privateMetadataUpdater, + blockHeader, + transactions, + miningBeneficiary, + blockHashLookup, + blobGasPrice); + + for (int i = 0; i < transactions.size(); i++) { + final Transaction transaction = transactions.get(i); if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) { return new BlockProcessingResult(Optional.empty(), "provided gas insufficient"); } + final WorldUpdater blockUpdater = worldState.updater(); - final WorldUpdater worldStateUpdater = worldState.updater(); - - final Address miningBeneficiary = - miningBeneficiaryCalculator.calculateBeneficiary(blockHeader); - - Optional maybeParentHeader = - blockchain.getBlockHeader(blockHeader.getParentHash()); - - Wei blobGasPrice = - maybeParentHeader - .map( - parentHeader -> - protocolSpec - .getFeeMarket() - .blobGasPricePerGas( - calculateExcessBlobGasForParent(protocolSpec, parentHeader))) - .orElse(Wei.ZERO); - - final TransactionProcessingResult result = - transactionProcessor.processTransaction( - worldStateUpdater, + TransactionProcessingResult transactionProcessingResult = + getTransactionProcessingResult( + preProcessingContext, + worldState, + blockUpdater, + privateMetadataUpdater, blockHeader, - transaction, + blobGasPrice, miningBeneficiary, - OperationTracer.NO_TRACING, - blockHashLookup, - true, - TransactionValidationParams.processingBlock(), - privateMetadataUpdater, - blobGasPrice); - if (result.isInvalid()) { + transaction, + i, + blockHashLookup); + if (transactionProcessingResult.isInvalid()) { String errorMessage = MessageFormat.format( "Block processing error: transaction invalid {0}. Block {1} Transaction {2}", - result.getValidationResult().getErrorMessage(), + transactionProcessingResult.getValidationResult().getErrorMessage(), blockHeader.getHash().toHexString(), transaction.getHash().toHexString()); LOG.info(errorMessage); if (worldState instanceof BonsaiWorldState) { - ((BonsaiWorldStateUpdateAccumulator) worldStateUpdater).reset(); + ((BonsaiWorldStateUpdateAccumulator) blockUpdater).reset(); } return new BlockProcessingResult(Optional.empty(), errorMessage); } - worldStateUpdater.commit(); - currentGasUsed += transaction.getGasLimit() - result.getGasRemaining(); + blockUpdater.commit(); + + currentGasUsed += transaction.getGasLimit() - transactionProcessingResult.getGasRemaining(); if (transaction.getVersionedHashes().isPresent()) { currentBlobGasUsed += (transaction.getVersionedHashes().get().size() * CancunGasCalculator.BLOB_GAS_PER_BLOB); @@ -168,7 +179,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { final TransactionReceipt transactionReceipt = transactionReceiptFactory.create( - transaction.getType(), result, worldState, currentGasUsed); + transaction.getType(), transactionProcessingResult, worldState, currentGasUsed); receipts.add(transactionReceipt); } if (blockHeader.getBlobGasUsed().isPresent() @@ -235,6 +246,41 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests))); } + protected Optional runBlockPreProcessing( + final MutableWorldState worldState, + final PrivateMetadataUpdater privateMetadataUpdater, + final BlockHeader blockHeader, + final List transactions, + final Address miningBeneficiary, + final BlockHashOperation.BlockHashLookup blockHashLookup, + final Wei blobGasPrice) { + return Optional.empty(); + } + + protected TransactionProcessingResult getTransactionProcessingResult( + final Optional preProcessingContext, + final MutableWorldState worldState, + final WorldUpdater blockUpdater, + final PrivateMetadataUpdater privateMetadataUpdater, + final BlockHeader blockHeader, + final Wei blobGasPrice, + final Address miningBeneficiary, + final Transaction transaction, + final int location, + final BlockHashLookup blockHashLookup) { + return transactionProcessor.processTransaction( + blockUpdater, + blockHeader, + transaction, + miningBeneficiary, + OperationTracer.NO_TRACING, + blockHashLookup, + true, + TransactionValidationParams.processingBlock(), + privateMetadataUpdater, + blobGasPrice); + } + protected boolean hasAvailableBlockBudget( final BlockHeader blockHeader, final Transaction transaction, final long currentGasUsed) { final long remainingGasBudget = blockHeader.getGasLimit() - currentGasUsed; @@ -261,4 +307,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { final BlockHeader header, final List ommers, final boolean skipZeroBlockRewards); + + public interface PreprocessingContext {} + ; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AuthorityProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AuthorityProcessor.java new file mode 100644 index 0000000000..f79e2c98e3 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AuthorityProcessor.java @@ -0,0 +1,88 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.account.AccountState; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.worldstate.AuthorizedCodeService; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.math.BigInteger; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AuthorityProcessor { + private static final Logger LOG = LoggerFactory.getLogger(AuthorityProcessor.class); + + private final Optional maybeChainId; + + public AuthorityProcessor(final Optional maybeChainId) { + this.maybeChainId = maybeChainId; + } + + public void addContractToAuthority( + final WorldUpdater worldUpdater, + final AuthorizedCodeService authorizedCodeService, + final Transaction transaction) { + + transaction + .getAuthorizationList() + .get() + .forEach( + payload -> + payload + .authorizer() + .ifPresent( + authorityAddress -> { + LOG.trace("Set code authority: {}", authorityAddress); + + if (maybeChainId.isPresent() + && !payload.chainId().equals(BigInteger.ZERO) + && !maybeChainId.get().equals(payload.chainId())) { + return; + } + + final Optional maybeAccount = + Optional.ofNullable(worldUpdater.getAccount(authorityAddress)); + final long accountNonce = + maybeAccount.map(AccountState::getNonce).orElse(0L); + + if (payload.nonce().isPresent() + && !payload.nonce().get().equals(accountNonce)) { + return; + } + + if (authorizedCodeService.hasAuthorizedCode(authorityAddress)) { + return; + } + + Optional codeAccount = + Optional.ofNullable(worldUpdater.get(payload.address())); + final Bytes code; + if (codeAccount.isPresent()) { + code = codeAccount.get().getCode(); + } else { + code = Bytes.EMPTY; + } + + authorizedCodeService.addAuthorizedCode(authorityAddress, code); + })); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java index aec844894f..47c1747dc9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicProtocolSpecs.java @@ -38,6 +38,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.processor.ContractCreationProcessor; import org.hyperledger.besu.evm.processor.MessageCallProcessor; import org.hyperledger.besu.evm.worldstate.WorldState; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; import java.util.Collections; @@ -54,16 +55,23 @@ public class ClassicProtocolSpecs { } public static ProtocolSpecBuilder classicRecoveryInitDefinition( - final EvmConfiguration evmConfiguration) { - return MainnetProtocolSpecs.homesteadDefinition(evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return MainnetProtocolSpecs.homesteadDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .blockHeaderValidatorBuilder( feeMarket -> MainnetBlockHeaderValidator.createClassicValidator()) .name("ClassicRecoveryInit"); } public static ProtocolSpecBuilder tangerineWhistleDefinition( - final Optional chainId, final EvmConfiguration evmConfiguration) { - return MainnetProtocolSpecs.homesteadDefinition(evmConfiguration) + final Optional chainId, + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return MainnetProtocolSpecs.homesteadDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .isReplayProtectionSupported(true) .gasCalculator(TangerineWhistleGasCalculator::new) .transactionValidatorFactoryBuilder( @@ -74,8 +82,12 @@ public class ClassicProtocolSpecs { } public static ProtocolSpecBuilder dieHardDefinition( - final Optional chainId, final EvmConfiguration evmConfiguration) { - return tangerineWhistleDefinition(chainId, evmConfiguration) + final Optional chainId, + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return tangerineWhistleDefinition( + chainId, evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .gasCalculator(DieHardGasCalculator::new) .difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_PAUSED) .name("DieHard"); @@ -84,8 +96,11 @@ public class ClassicProtocolSpecs { public static ProtocolSpecBuilder gothamDefinition( final Optional chainId, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return dieHardDefinition(chainId, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return dieHardDefinition( + chainId, evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .blockReward(MAX_BLOCK_REWARD) .difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_DELAYED) .blockProcessorBuilder( @@ -109,8 +124,15 @@ public class ClassicProtocolSpecs { public static ProtocolSpecBuilder defuseDifficultyBombDefinition( final Optional chainId, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return gothamDefinition(chainId, ecip1017EraRounds, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return gothamDefinition( + chainId, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .difficultyCalculator(ClassicDifficultyCalculators.DIFFICULTY_BOMB_REMOVED) .transactionValidatorFactoryBuilder( (evm, gasLimitCalculator, feeMarket) -> @@ -123,8 +145,15 @@ public class ClassicProtocolSpecs { final Optional chainId, final boolean enableRevertReason, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return gothamDefinition(chainId, ecip1017EraRounds, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return gothamDefinition( + chainId, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .evmBuilder(MainnetEVMs::byzantium) .evmConfiguration(evmConfiguration) .gasCalculator(SpuriousDragonGasCalculator::new) @@ -163,8 +192,16 @@ public class ClassicProtocolSpecs { final Optional chainId, final boolean enableRevertReason, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return atlantisDefinition(chainId, enableRevertReason, ecip1017EraRounds, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return atlantisDefinition( + chainId, + enableRevertReason, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .evmBuilder(MainnetEVMs::constantinople) .gasCalculator(PetersburgGasCalculator::new) .evmBuilder(MainnetEVMs::constantinople) @@ -176,8 +213,16 @@ public class ClassicProtocolSpecs { final Optional chainId, final boolean enableRevertReason, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return aghartaDefinition(chainId, enableRevertReason, ecip1017EraRounds, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return aghartaDefinition( + chainId, + enableRevertReason, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .gasCalculator(IstanbulGasCalculator::new) .evmBuilder( (gasCalculator, evmConfig) -> @@ -191,8 +236,16 @@ public class ClassicProtocolSpecs { final Optional chainId, final boolean enableRevertReason, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return phoenixDefinition(chainId, enableRevertReason, ecip1017EraRounds, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return phoenixDefinition( + chainId, + enableRevertReason, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .blockHeaderValidatorBuilder( feeMarket -> MainnetBlockHeaderValidator.createPgaBlockHeaderValidator( @@ -228,8 +281,16 @@ public class ClassicProtocolSpecs { final Optional chainId, final boolean enableRevertReason, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return thanosDefinition(chainId, enableRevertReason, ecip1017EraRounds, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return thanosDefinition( + chainId, + enableRevertReason, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .gasCalculator(BerlinGasCalculator::new) .transactionValidatorFactoryBuilder( (evm, gasLimitCalculator, feeMarket) -> @@ -250,8 +311,16 @@ public class ClassicProtocolSpecs { final Optional chainId, final boolean enableRevertReason, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return magnetoDefinition(chainId, enableRevertReason, ecip1017EraRounds, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return magnetoDefinition( + chainId, + enableRevertReason, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .gasCalculator(LondonGasCalculator::new) .contractCreationProcessorBuilder( evm -> @@ -264,8 +333,16 @@ public class ClassicProtocolSpecs { final Optional chainId, final boolean enableRevertReason, final OptionalLong ecip1017EraRounds, - final EvmConfiguration evmConfiguration) { - return mystiqueDefinition(chainId, enableRevertReason, ecip1017EraRounds, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return mystiqueDefinition( + chainId, + enableRevertReason, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) // EIP-3860 .gasCalculator(ShanghaiGasCalculator::new) // EIP-3855 diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java index 794f96cfb3..88b6a5ae1b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyCalculators; import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSchedule; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; import java.util.function.Function; @@ -40,6 +41,8 @@ public class MainnetProtocolSchedule { * @param evmConfiguration how to configure the EVMs jumpdest cache * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled + * @param metricsSystem A metricSystem instance to expose metrics in the underlying calls * @return A configured mainnet protocol schedule */ public static ProtocolSchedule fromConfig( @@ -48,7 +51,9 @@ public class MainnetProtocolSchedule { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { if (FixedDifficultyCalculators.isFixedDifficultyInConfig(config)) { return FixedDifficultyProtocolSchedule.create( config, @@ -56,7 +61,9 @@ public class MainnetProtocolSchedule { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } return new ProtocolScheduleBuilder( config, @@ -66,7 +73,9 @@ public class MainnetProtocolSchedule { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager) + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem) .createProtocolSchedule(); } @@ -79,6 +88,7 @@ public class MainnetProtocolSchedule { * @param evmConfiguration how to configure the EVMs jumpdest cache * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. * @return A configured mainnet protocol schedule */ public static ProtocolSchedule fromConfig( @@ -86,14 +96,18 @@ public class MainnetProtocolSchedule { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return fromConfig( config, PrivacyParameters.DEFAULT, isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } /** @@ -104,20 +118,25 @@ public class MainnetProtocolSchedule { * @param evmConfiguration size of * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. * @return A configured mainnet protocol schedule */ public static ProtocolSchedule fromConfig( final GenesisConfigOptions config, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return fromConfig( config, PrivacyParameters.DEFAULT, false, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } /** @@ -127,18 +146,23 @@ public class MainnetProtocolSchedule { * starting points * @param miningParameters the mining parameters * @param badBlockManager the cache to use to keep invalid blocks + * @param isParallelTxProcessingEnabled indicates whether parallel transaction is enabled. * @return A configured mainnet protocol schedule */ public static ProtocolSchedule fromConfig( final GenesisConfigOptions config, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return fromConfig( config, PrivacyParameters.DEFAULT, false, EvmConfiguration.DEFAULT, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java index aa9bae6c7e..55bfb363ba 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet; import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; import java.util.Optional; @@ -29,118 +30,209 @@ public class MainnetProtocolSpecFactory { private final OptionalLong ecip1017EraRounds; private final EvmConfiguration evmConfiguration; private final MiningParameters miningParameters; + private final boolean isParallelTxProcessingEnabled; + private final MetricsSystem metricsSystem; public MainnetProtocolSpecFactory( final Optional chainId, final boolean isRevertReasonEnabled, final OptionalLong ecip1017EraRounds, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { this.chainId = chainId; this.isRevertReasonEnabled = isRevertReasonEnabled; this.ecip1017EraRounds = ecip1017EraRounds; this.evmConfiguration = evmConfiguration; this.miningParameters = miningParameters; + this.isParallelTxProcessingEnabled = isParallelTxProcessingEnabled; + this.metricsSystem = metricsSystem; } public ProtocolSpecBuilder frontierDefinition() { - return MainnetProtocolSpecs.frontierDefinition(evmConfiguration); + return MainnetProtocolSpecs.frontierDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder homesteadDefinition() { - return MainnetProtocolSpecs.homesteadDefinition(evmConfiguration); + return MainnetProtocolSpecs.homesteadDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder daoRecoveryInitDefinition() { - return MainnetProtocolSpecs.daoRecoveryInitDefinition(evmConfiguration); + return MainnetProtocolSpecs.daoRecoveryInitDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder daoRecoveryTransitionDefinition() { - return MainnetProtocolSpecs.daoRecoveryTransitionDefinition(evmConfiguration); + return MainnetProtocolSpecs.daoRecoveryTransitionDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder tangerineWhistleDefinition() { - return MainnetProtocolSpecs.tangerineWhistleDefinition(evmConfiguration); + return MainnetProtocolSpecs.tangerineWhistleDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder spuriousDragonDefinition() { - return MainnetProtocolSpecs.spuriousDragonDefinition(chainId, evmConfiguration); + return MainnetProtocolSpecs.spuriousDragonDefinition( + chainId, evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder byzantiumDefinition() { return MainnetProtocolSpecs.byzantiumDefinition( - chainId, isRevertReasonEnabled, evmConfiguration); + chainId, + isRevertReasonEnabled, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder constantinopleDefinition() { return MainnetProtocolSpecs.constantinopleDefinition( - chainId, isRevertReasonEnabled, evmConfiguration); + chainId, + isRevertReasonEnabled, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder petersburgDefinition() { return MainnetProtocolSpecs.petersburgDefinition( - chainId, isRevertReasonEnabled, evmConfiguration); + chainId, + isRevertReasonEnabled, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder istanbulDefinition() { return MainnetProtocolSpecs.istanbulDefinition( - chainId, isRevertReasonEnabled, evmConfiguration); + chainId, + isRevertReasonEnabled, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder muirGlacierDefinition() { return MainnetProtocolSpecs.muirGlacierDefinition( - chainId, isRevertReasonEnabled, evmConfiguration); + chainId, + isRevertReasonEnabled, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder berlinDefinition() { - return MainnetProtocolSpecs.berlinDefinition(chainId, isRevertReasonEnabled, evmConfiguration); + return MainnetProtocolSpecs.berlinDefinition( + chainId, + isRevertReasonEnabled, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder londonDefinition(final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.londonDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder arrowGlacierDefinition( final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.arrowGlacierDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder grayGlacierDefinition( final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.grayGlacierDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder parisDefinition(final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.parisDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder shanghaiDefinition(final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.shanghaiDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder cancunDefinition(final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.cancunDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder cancunEOFDefinition(final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.cancunEOFDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder pragueDefinition(final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.pragueDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder pragueEOFDefinition(final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.pragueEOFDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } /** @@ -156,7 +248,13 @@ public class MainnetProtocolSpecFactory { */ public ProtocolSpecBuilder futureEipsDefinition(final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.futureEipsDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } /** @@ -172,57 +270,100 @@ public class MainnetProtocolSpecFactory { public ProtocolSpecBuilder experimentalEipsDefinition( final GenesisConfigOptions genesisConfigOptions) { return MainnetProtocolSpecs.experimentalEipsDefinition( - chainId, isRevertReasonEnabled, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + isRevertReasonEnabled, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); } //////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// // Classic Protocol Specs public ProtocolSpecBuilder dieHardDefinition() { - return ClassicProtocolSpecs.dieHardDefinition(chainId, evmConfiguration); + return ClassicProtocolSpecs.dieHardDefinition( + chainId, evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder gothamDefinition() { - return ClassicProtocolSpecs.gothamDefinition(chainId, ecip1017EraRounds, evmConfiguration); + return ClassicProtocolSpecs.gothamDefinition( + chainId, ecip1017EraRounds, evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder defuseDifficultyBombDefinition() { return ClassicProtocolSpecs.defuseDifficultyBombDefinition( - chainId, ecip1017EraRounds, evmConfiguration); + chainId, ecip1017EraRounds, evmConfiguration, isParallelTxProcessingEnabled, metricsSystem); } public ProtocolSpecBuilder atlantisDefinition() { return ClassicProtocolSpecs.atlantisDefinition( - chainId, isRevertReasonEnabled, ecip1017EraRounds, evmConfiguration); + chainId, + isRevertReasonEnabled, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder aghartaDefinition() { return ClassicProtocolSpecs.aghartaDefinition( - chainId, isRevertReasonEnabled, ecip1017EraRounds, evmConfiguration); + chainId, + isRevertReasonEnabled, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder phoenixDefinition() { return ClassicProtocolSpecs.phoenixDefinition( - chainId, isRevertReasonEnabled, ecip1017EraRounds, evmConfiguration); + chainId, + isRevertReasonEnabled, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder thanosDefinition() { return ClassicProtocolSpecs.thanosDefinition( - chainId, isRevertReasonEnabled, ecip1017EraRounds, evmConfiguration); + chainId, + isRevertReasonEnabled, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder magnetoDefinition() { return ClassicProtocolSpecs.magnetoDefinition( - chainId, isRevertReasonEnabled, ecip1017EraRounds, evmConfiguration); + chainId, + isRevertReasonEnabled, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder mystiqueDefinition() { return ClassicProtocolSpecs.mystiqueDefinition( - chainId, isRevertReasonEnabled, ecip1017EraRounds, evmConfiguration); + chainId, + isRevertReasonEnabled, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolSpecBuilder spiralDefinition() { return ClassicProtocolSpecs.spiralDefinition( - chainId, isRevertReasonEnabled, ecip1017EraRounds, evmConfiguration); + chainId, + isRevertReasonEnabled, + ecip1017EraRounds, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 4ae30f8de9..36d8257ffb 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.mainnet.blockhash.FrontierBlockHashProcesso import org.hyperledger.besu.ethereum.mainnet.blockhash.PragueBlockHashProcessor; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; +import org.hyperledger.besu.ethereum.mainnet.parallelization.MainnetParallelBlockProcessor; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator; import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; @@ -67,6 +68,7 @@ import org.hyperledger.besu.evm.processor.ContractCreationProcessor; import org.hyperledger.besu.evm.processor.MessageCallProcessor; import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.evm.worldstate.WorldUpdater; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.io.IOException; import java.math.BigInteger; @@ -101,7 +103,10 @@ public abstract class MainnetProtocolSpecs { private MainnetProtocolSpecs() {} - public static ProtocolSpecBuilder frontierDefinition(final EvmConfiguration evmConfiguration) { + public static ProtocolSpecBuilder frontierDefinition( + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return new ProtocolSpecBuilder() .gasCalculator(FrontierGasCalculator::new) .gasLimitCalculatorBuilder(feeMarket -> new FrontierTargetingGasLimitCalculator()) @@ -152,7 +157,10 @@ public abstract class MainnetProtocolSpecs { .transactionReceiptFactory(MainnetProtocolSpecs::frontierTransactionReceiptFactory) .blockReward(FRONTIER_BLOCK_REWARD) .skipZeroBlockRewards(false) - .blockProcessorBuilder(MainnetBlockProcessor::new) + .blockProcessorBuilder( + isParallelTxProcessingEnabled + ? new MainnetParallelBlockProcessor.ParallelBlockProcessorBuilder(metricsSystem) + : MainnetBlockProcessor::new) .blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder()) .blockImporterBuilder(MainnetBlockImporter::new) .blockHeaderFunctions(new MainnetBlockHeaderFunctions()) @@ -173,8 +181,11 @@ public abstract class MainnetProtocolSpecs { return MainnetBlockValidator::new; } - public static ProtocolSpecBuilder homesteadDefinition(final EvmConfiguration evmConfiguration) { - return frontierDefinition(evmConfiguration) + public static ProtocolSpecBuilder homesteadDefinition( + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return frontierDefinition(evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .gasCalculator(HomesteadGasCalculator::new) .evmBuilder(MainnetEVMs::homestead) .contractCreationProcessorBuilder( @@ -190,8 +201,10 @@ public abstract class MainnetProtocolSpecs { } public static ProtocolSpecBuilder daoRecoveryInitDefinition( - final EvmConfiguration evmConfiguration) { - return homesteadDefinition(evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return homesteadDefinition(evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .blockHeaderValidatorBuilder(feeMarket -> MainnetBlockHeaderValidator.createDaoValidator()) .blockProcessorBuilder( (transactionProcessor, @@ -201,33 +214,53 @@ public abstract class MainnetProtocolSpecs { skipZeroBlockRewards, protocolSchedule) -> new DaoBlockProcessor( - new MainnetBlockProcessor( - transactionProcessor, - transactionReceiptFactory, - blockReward, - miningBeneficiaryCalculator, - skipZeroBlockRewards, - protocolSchedule))) + isParallelTxProcessingEnabled + ? new MainnetParallelBlockProcessor( + transactionProcessor, + transactionReceiptFactory, + blockReward, + miningBeneficiaryCalculator, + skipZeroBlockRewards, + protocolSchedule, + metricsSystem) + : new MainnetBlockProcessor( + transactionProcessor, + transactionReceiptFactory, + blockReward, + miningBeneficiaryCalculator, + skipZeroBlockRewards, + protocolSchedule))) .name("DaoRecoveryInit"); } public static ProtocolSpecBuilder daoRecoveryTransitionDefinition( - final EvmConfiguration evmConfiguration) { - return daoRecoveryInitDefinition(evmConfiguration) - .blockProcessorBuilder(MainnetBlockProcessor::new) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return daoRecoveryInitDefinition(evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) + .blockProcessorBuilder( + isParallelTxProcessingEnabled + ? new MainnetParallelBlockProcessor.ParallelBlockProcessorBuilder(metricsSystem) + : MainnetBlockProcessor::new) .name("DaoRecoveryTransition"); } public static ProtocolSpecBuilder tangerineWhistleDefinition( - final EvmConfiguration evmConfiguration) { - return homesteadDefinition(evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return homesteadDefinition(evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .gasCalculator(TangerineWhistleGasCalculator::new) .name("TangerineWhistle"); } public static ProtocolSpecBuilder spuriousDragonDefinition( - final Optional chainId, final EvmConfiguration evmConfiguration) { - return tangerineWhistleDefinition(evmConfiguration) + final Optional chainId, + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return tangerineWhistleDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .isReplayProtectionSupported(true) .gasCalculator(SpuriousDragonGasCalculator::new) .skipZeroBlockRewards(true) @@ -271,8 +304,11 @@ public abstract class MainnetProtocolSpecs { public static ProtocolSpecBuilder byzantiumDefinition( final Optional chainId, final boolean enableRevertReason, - final EvmConfiguration evmConfiguration) { - return spuriousDragonDefinition(chainId, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return spuriousDragonDefinition( + chainId, evmConfiguration, isParallelTxProcessingEnabled, metricsSystem) .gasCalculator(ByzantiumGasCalculator::new) .evmBuilder(MainnetEVMs::byzantium) .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::byzantium) @@ -301,8 +337,15 @@ public abstract class MainnetProtocolSpecs { public static ProtocolSpecBuilder constantinopleDefinition( final Optional chainId, final boolean enableRevertReason, - final EvmConfiguration evmConfiguration) { - return byzantiumDefinition(chainId, enableRevertReason, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return byzantiumDefinition( + chainId, + enableRevertReason, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .difficultyCalculator(MainnetDifficultyCalculators.CONSTANTINOPLE) .gasCalculator(ConstantinopleGasCalculator::new) .evmBuilder(MainnetEVMs::constantinople) @@ -313,8 +356,15 @@ public abstract class MainnetProtocolSpecs { public static ProtocolSpecBuilder petersburgDefinition( final Optional chainId, final boolean enableRevertReason, - final EvmConfiguration evmConfiguration) { - return constantinopleDefinition(chainId, enableRevertReason, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return constantinopleDefinition( + chainId, + enableRevertReason, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .gasCalculator(PetersburgGasCalculator::new) .name("Petersburg"); } @@ -322,8 +372,15 @@ public abstract class MainnetProtocolSpecs { public static ProtocolSpecBuilder istanbulDefinition( final Optional chainId, final boolean enableRevertReason, - final EvmConfiguration evmConfiguration) { - return petersburgDefinition(chainId, enableRevertReason, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return petersburgDefinition( + chainId, + enableRevertReason, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .gasCalculator(IstanbulGasCalculator::new) .evmBuilder( (gasCalculator, jdCacheConfig) -> @@ -344,8 +401,15 @@ public abstract class MainnetProtocolSpecs { static ProtocolSpecBuilder muirGlacierDefinition( final Optional chainId, final boolean enableRevertReason, - final EvmConfiguration evmConfiguration) { - return istanbulDefinition(chainId, enableRevertReason, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return istanbulDefinition( + chainId, + enableRevertReason, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .difficultyCalculator(MainnetDifficultyCalculators.MUIR_GLACIER) .name("MuirGlacier"); } @@ -353,8 +417,15 @@ public abstract class MainnetProtocolSpecs { static ProtocolSpecBuilder berlinDefinition( final Optional chainId, final boolean enableRevertReason, - final EvmConfiguration evmConfiguration) { - return muirGlacierDefinition(chainId, enableRevertReason, evmConfiguration) + final EvmConfiguration evmConfiguration, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { + return muirGlacierDefinition( + chainId, + enableRevertReason, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .gasCalculator(BerlinGasCalculator::new) .transactionValidatorFactoryBuilder( (evm, gasLimitCalculator, feeMarket) -> @@ -376,7 +447,9 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(Long.MAX_VALUE); final BaseFeeMarket londonFeeMarket; @@ -390,7 +463,12 @@ public abstract class MainnetProtocolSpecs { londonFeeMarket = FeeMarket.london(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); } - return berlinDefinition(chainId, enableRevertReason, evmConfiguration) + return berlinDefinition( + chainId, + enableRevertReason, + evmConfiguration, + isParallelTxProcessingEnabled, + metricsSystem) .feeMarket(londonFeeMarket) .gasCalculator(LondonGasCalculator::new) .gasLimitCalculatorBuilder( @@ -455,9 +533,17 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return londonDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem) .difficultyCalculator(MainnetDifficultyCalculators.ARROW_GLACIER) .name("ArrowGlacier"); } @@ -467,9 +553,17 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return arrowGlacierDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem) .difficultyCalculator(MainnetDifficultyCalculators.GRAY_GLACIER) .name("GrayGlacier"); } @@ -479,10 +573,18 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return grayGlacierDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem) .evmBuilder( (gasCalculator, jdCacheConfig) -> MainnetEVMs.paris(gasCalculator, chainId.orElse(BigInteger.ZERO), evmConfiguration)) @@ -499,9 +601,17 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return parisDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem) // gas calculator has new code to support EIP-3860 limit and meter initcode .gasCalculator(ShanghaiGasCalculator::new) // EVM has a new operation for EIP-3855 PUSH0 instruction @@ -550,7 +660,9 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(0L); final BaseFeeMarket cancunFeeMarket; if (genesisConfigOptions.isZeroBaseFee()) { @@ -565,7 +677,13 @@ public abstract class MainnetProtocolSpecs { } return shanghaiDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem) .feeMarket(cancunFeeMarket) // gas calculator for EIP-4844 blob gas .gasCalculator(CancunGasCalculator::new) @@ -595,7 +713,8 @@ public abstract class MainnetProtocolSpecs { true, evmConfiguration.evmStackSize(), feeMarket, - CoinbaseFeePriceCalculator.eip1559())) + CoinbaseFeePriceCalculator.eip1559(), + new AuthorityProcessor(chainId))) // change to check for max blob gas per block for EIP-4844 .transactionValidatorFactoryBuilder( (evm, gasLimitCalculator, feeMarket) -> @@ -622,11 +741,19 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { ProtocolSpecBuilder protocolSpecBuilder = cancunDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); return addEOF(chainId, evmConfiguration, protocolSpecBuilder).name("CancunEOF"); } @@ -635,14 +762,22 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { final Address depositContractAddress = genesisConfigOptions.getDepositContractAddress().orElse(DEFAULT_DEPOSIT_CONTRACT_ADDRESS); return cancunDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) - // EIP-3074 AUTH and AUTHCALL gas + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem) + // EIP-3074 AUTH and AUTCALL gas .gasCalculator(PragueGasCalculator::new) // EIP-3074 AUTH and AUTHCALL .evmBuilder( @@ -658,6 +793,23 @@ public abstract class MainnetProtocolSpecs { // EIP-7002 Withdrawals / EIP-6610 Deposits / EIP-7685 Requests .requestProcessorCoordinator(pragueRequestsProcessors(depositContractAddress)) + // change to accept EIP-7702 transactions + .transactionValidatorFactoryBuilder( + (evm, gasLimitCalculator, feeMarket) -> + new TransactionValidatorFactory( + evm.getGasCalculator(), + gasLimitCalculator, + feeMarket, + true, + chainId, + Set.of( + TransactionType.FRONTIER, + TransactionType.ACCESS_LIST, + TransactionType.EIP1559, + TransactionType.BLOB, + TransactionType.SET_CODE), + evm.getEvmVersion().getMaxInitcodeSize())) + // EIP-2935 Blockhash processor .blockHashProcessor(new PragueBlockHashProcessor()) .name("Prague"); @@ -668,11 +820,19 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { ProtocolSpecBuilder protocolSpecBuilder = pragueDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters); + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); return addEOF(chainId, evmConfiguration, protocolSpecBuilder).name("PragueEOF"); } @@ -704,9 +864,17 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return pragueEOFDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem) // Use Future EIP configured EVM .evmBuilder( (gasCalculator, jdCacheConfig) -> @@ -731,10 +899,18 @@ public abstract class MainnetProtocolSpecs { final boolean enableRevertReason, final GenesisConfigOptions genesisConfigOptions, final EvmConfiguration evmConfiguration, - final MiningParameters miningParameters) { + final MiningParameters miningParameters, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { return futureEipsDefinition( - chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) + chainId, + enableRevertReason, + genesisConfigOptions, + evmConfiguration, + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem) .evmBuilder( (gasCalculator, jdCacheConfig) -> MainnetEVMs.experimentalEips( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 53801d14cd..5fa2b119f1 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -42,6 +42,7 @@ import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.processor.AbstractMessageProcessor; import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.worldstate.AuthorizedCodeService; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.Deque; @@ -80,6 +81,8 @@ public class MainnetTransactionProcessor { protected final FeeMarket feeMarket; private final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator; + private final Optional maybeAuthorityProcessor; + public MainnetTransactionProcessor( final GasCalculator gasCalculator, final TransactionValidatorFactory transactionValidatorFactory, @@ -90,6 +93,30 @@ public class MainnetTransactionProcessor { final int maxStackSize, final FeeMarket feeMarket, final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator) { + this( + gasCalculator, + transactionValidatorFactory, + contractCreationProcessor, + messageCallProcessor, + clearEmptyAccounts, + warmCoinbase, + maxStackSize, + feeMarket, + coinbaseFeePriceCalculator, + null); + } + + public MainnetTransactionProcessor( + final GasCalculator gasCalculator, + final TransactionValidatorFactory transactionValidatorFactory, + final AbstractMessageProcessor contractCreationProcessor, + final AbstractMessageProcessor messageCallProcessor, + final boolean clearEmptyAccounts, + final boolean warmCoinbase, + final int maxStackSize, + final FeeMarket feeMarket, + final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator, + final AuthorityProcessor maybeAuthorityProcessor) { this.gasCalculator = gasCalculator; this.transactionValidatorFactory = transactionValidatorFactory; this.contractCreationProcessor = contractCreationProcessor; @@ -99,6 +126,7 @@ public class MainnetTransactionProcessor { this.maxStackSize = maxStackSize; this.feeMarket = feeMarket; this.coinbaseFeePriceCalculator = coinbaseFeePriceCalculator; + this.maybeAuthorityProcessor = Optional.ofNullable(maybeAuthorityProcessor); } /** @@ -259,6 +287,8 @@ public class MainnetTransactionProcessor { final PrivateMetadataUpdater privateMetadataUpdater, final Wei blobGasPrice) { try { + final AuthorizedCodeService authorizedCodeService = new AuthorizedCodeService(); + worldState.setAuthorizedCodeService(authorizedCodeService); final var transactionValidator = transactionValidatorFactory.get(); LOG.trace("Starting execution of {}", transaction); ValidationResult validationResult = @@ -332,15 +362,19 @@ public class MainnetTransactionProcessor { transaction.getPayload(), transaction.isContractCreation()); final long accessListGas = gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount); - final long gasAvailable = transaction.getGasLimit() - intrinsicGas - accessListGas; + final long setCodeGas = gasCalculator.setCodeListGasCost(transaction.authorizationListSize()); + final long gasAvailable = + transaction.getGasLimit() - intrinsicGas - accessListGas - setCodeGas; LOG.trace( - "Gas available for execution {} = {} - {} - {} (limit - intrinsic - accessList)", + "Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - setCode)", gasAvailable, transaction.getGasLimit(), intrinsicGas, - accessListGas); + accessListGas, + setCodeGas); final WorldUpdater worldUpdater = worldState.updater(); + worldUpdater.setAuthorizedCodeService(authorizedCodeService); final ImmutableMap.Builder contextVariablesBuilder = ImmutableMap.builder() .put(KEY_IS_PERSISTING_PRIVATE_STATE, isPersistingPrivateState) @@ -374,6 +408,15 @@ public class MainnetTransactionProcessor { if (transaction.getVersionedHashes().isPresent()) { commonMessageFrameBuilder.versionedHashes( Optional.of(transaction.getVersionedHashes().get().stream().toList())); + } else if (transaction.getAuthorizationList().isPresent()) { + if (maybeAuthorityProcessor.isEmpty()) { + throw new RuntimeException("Authority processor is required for 7702 transactions"); + } + + maybeAuthorityProcessor + .get() + .addContractToAuthority(worldUpdater, authorizedCodeService, transaction); + addressList.addAll(authorizedCodeService.getAuthorities()); } else { commonMessageFrameBuilder.versionedHashes(Optional.empty()); } @@ -392,6 +435,7 @@ public class MainnetTransactionProcessor { .contract(contractAddress) .inputData(initCodeBytes.slice(code.getSize())) .code(code) + .authorizedCodeService(authorizedCodeService) .build(); } else { @SuppressWarnings("OptionalGetWithoutIsPresent") // isContractCall tests isPresent @@ -407,6 +451,7 @@ public class MainnetTransactionProcessor { maybeContract .map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode())) .orElse(CodeV0.EMPTY_CODE)) + .authorizedCodeService(authorizedCodeService) .build(); } Deque messageFrameStack = initialFrame.getMessageFrameStack(); @@ -462,7 +507,6 @@ public class MainnetTransactionProcessor { final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas(); // update the coinbase - final var coinbase = worldState.getOrCreate(miningBeneficiary); final long usedGas = transaction.getGasLimit() - refundedGas; final CoinbaseFeePriceCalculator coinbaseCalculator; if (blockHeader.getBaseFee().isPresent()) { @@ -484,7 +528,11 @@ public class MainnetTransactionProcessor { final Wei coinbaseWeiDelta = coinbaseCalculator.price(usedGas, transactionGasPrice, blockHeader.getBaseFee()); + operationTracer.traceBeforeRewardTransaction(worldUpdater, transaction, coinbaseWeiDelta); + + final var coinbase = worldState.getOrCreate(miningBeneficiary); coinbase.incrementBalance(coinbaseWeiDelta); + authorizedCodeService.resetAuthorities(); operationTracer.traceEndTransaction( worldUpdater, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index 9d90a409a5..481ce70e05 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -189,7 +189,8 @@ public class MainnetTransactionValidator implements TransactionValidator { final long intrinsicGasCost = gasCalculator.transactionIntrinsicGasCost( transaction.getPayload(), transaction.isContractCreation()) - + (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L)); + + (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L)) + + gasCalculator.setCodeListGasCost(transaction.authorizationListSize()); if (Long.compareUnsigned(intrinsicGasCost, transaction.getGasLimit()) > 0) { return ValidationResult.invalid( TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java index 649442f88c..1cb3a5cb3c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; import java.util.NavigableMap; @@ -45,6 +46,8 @@ public class ProtocolScheduleBuilder { private final EvmConfiguration evmConfiguration; private final MiningParameters miningParameters; private final BadBlockManager badBlockManager; + private final boolean isParallelTxProcessingEnabled; + private final MetricsSystem metricsSystem; public ProtocolScheduleBuilder( final GenesisConfigOptions config, @@ -54,7 +57,9 @@ public class ProtocolScheduleBuilder { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { this( config, Optional.of(defaultChainId), @@ -63,7 +68,9 @@ public class ProtocolScheduleBuilder { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } public ProtocolScheduleBuilder( @@ -73,7 +80,9 @@ public class ProtocolScheduleBuilder { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { this( config, Optional.empty(), @@ -82,7 +91,9 @@ public class ProtocolScheduleBuilder { isRevertReasonEnabled, evmConfiguration, miningParameters, - badBlockManager); + badBlockManager, + isParallelTxProcessingEnabled, + metricsSystem); } private ProtocolScheduleBuilder( @@ -93,7 +104,9 @@ public class ProtocolScheduleBuilder { final boolean isRevertReasonEnabled, final EvmConfiguration evmConfiguration, final MiningParameters miningParameters, - final BadBlockManager badBlockManager) { + final BadBlockManager badBlockManager, + final boolean isParallelTxProcessingEnabled, + final MetricsSystem metricsSystem) { this.config = config; this.protocolSpecAdapters = protocolSpecAdapters; this.privacyParameters = privacyParameters; @@ -102,6 +115,8 @@ public class ProtocolScheduleBuilder { this.defaultChainId = defaultChainId; this.miningParameters = miningParameters; this.badBlockManager = badBlockManager; + this.isParallelTxProcessingEnabled = isParallelTxProcessingEnabled; + this.metricsSystem = metricsSystem; } public ProtocolSchedule createProtocolSchedule() { @@ -121,7 +136,9 @@ public class ProtocolScheduleBuilder { config.getEcip1017EraRounds(), evmConfiguration.overrides( config.getContractSizeLimit(), OptionalInt.empty(), config.getEvmStackSize()), - miningParameters); + miningParameters, + isParallelTxProcessingEnabled, + metricsSystem); validateForkOrdering(); @@ -203,7 +220,8 @@ public class ProtocolScheduleBuilder { protocolSchedule, BuilderMapEntry.MilestoneType.BLOCK_NUMBER, classicBlockNumber, - ClassicProtocolSpecs.classicRecoveryInitDefinition(evmConfiguration), + ClassicProtocolSpecs.classicRecoveryInitDefinition( + evmConfiguration, isParallelTxProcessingEnabled, metricsSystem), Function.identity()); protocolSchedule.putBlockNumberMilestone( classicBlockNumber + 1, originalProtocolSpec); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java index cd886b6c84..0d792b7870 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java @@ -51,6 +51,7 @@ public class ProtocolSpecBuilder { private Function gasLimitCalculatorBuilder; private Wei blockReward; private boolean skipZeroBlockRewards; + private BlockHeaderFunctions blockHeaderFunctions; private AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory; private DifficultyCalculator difficultyCalculator; @@ -66,9 +67,11 @@ public class ProtocolSpecBuilder { private BiFunction messageCallProcessorBuilder; private TransactionProcessorBuilder transactionProcessorBuilder; + private BlockProcessorBuilder blockProcessorBuilder; private BlockValidatorBuilder blockValidatorBuilder; private BlockImporterBuilder blockImporterBuilder; + private String name; private MiningBeneficiaryCalculator miningBeneficiaryCalculator; private PrivacyParameters privacyParameters; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/MainnetParallelBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/MainnetParallelBlockProcessor.java new file mode 100644 index 0000000000..d1f0d9c512 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/MainnetParallelBlockProcessor.java @@ -0,0 +1,199 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.parallelization; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.BlockProcessor; +import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; +import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; +import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldState; +import org.hyperledger.besu.evm.operation.BlockHashOperation; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; +import org.hyperledger.besu.metrics.BesuMetricCategory; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.metrics.Counter; + +import java.util.List; +import java.util.Optional; + +public class MainnetParallelBlockProcessor extends MainnetBlockProcessor { + + private final Optional metricsSystem; + private final Optional confirmedParallelizedTransactionCounter; + private final Optional conflictingButCachedTransactionCounter; + + public MainnetParallelBlockProcessor( + final MainnetTransactionProcessor transactionProcessor, + final TransactionReceiptFactory transactionReceiptFactory, + final Wei blockReward, + final MiningBeneficiaryCalculator miningBeneficiaryCalculator, + final boolean skipZeroBlockRewards, + final ProtocolSchedule protocolSchedule, + final MetricsSystem metricsSystem) { + super( + transactionProcessor, + transactionReceiptFactory, + blockReward, + miningBeneficiaryCalculator, + skipZeroBlockRewards, + protocolSchedule); + this.metricsSystem = Optional.of(metricsSystem); + this.confirmedParallelizedTransactionCounter = + Optional.of( + this.metricsSystem + .get() + .createCounter( + BesuMetricCategory.BLOCK_PROCESSING, + "parallelized_transactions_counter", + "Counter for the number of parallelized transactions during block processing")); + + this.conflictingButCachedTransactionCounter = + Optional.of( + this.metricsSystem + .get() + .createCounter( + BesuMetricCategory.BLOCK_PROCESSING, + "conflicted_transactions_counter", + "Counter for the number of conflicted transactions during block processing")); + } + + @Override + protected Optional runBlockPreProcessing( + final MutableWorldState worldState, + final PrivateMetadataUpdater privateMetadataUpdater, + final BlockHeader blockHeader, + final List transactions, + final Address miningBeneficiary, + final BlockHashOperation.BlockHashLookup blockHashLookup, + final Wei blobGasPrice) { + if ((worldState instanceof DiffBasedWorldState)) { + ParallelizedConcurrentTransactionProcessor parallelizedConcurrentTransactionProcessor = + new ParallelizedConcurrentTransactionProcessor(transactionProcessor); + // runAsyncBlock, if activated, facilitates the non-blocking parallel execution of + // transactions in the background through an optimistic strategy. + parallelizedConcurrentTransactionProcessor.runAsyncBlock( + worldState, + blockHeader, + transactions, + miningBeneficiary, + blockHashLookup, + blobGasPrice, + privateMetadataUpdater); + return Optional.of( + new ParallelizedPreProcessingContext(parallelizedConcurrentTransactionProcessor)); + } + return Optional.empty(); + } + + @Override + protected TransactionProcessingResult getTransactionProcessingResult( + final Optional preProcessingContext, + final MutableWorldState worldState, + final WorldUpdater blockUpdater, + final PrivateMetadataUpdater privateMetadataUpdater, + final BlockHeader blockHeader, + final Wei blobGasPrice, + final Address miningBeneficiary, + final Transaction transaction, + final int location, + final BlockHashOperation.BlockHashLookup blockHashLookup) { + + TransactionProcessingResult transactionProcessingResult = null; + + if (preProcessingContext.isPresent()) { + final ParallelizedPreProcessingContext parallelizedPreProcessingContext = + (ParallelizedPreProcessingContext) preProcessingContext.get(); + transactionProcessingResult = + parallelizedPreProcessingContext + .getParallelizedConcurrentTransactionProcessor() + .applyParallelizedTransactionResult( + worldState, + miningBeneficiary, + transaction, + location, + confirmedParallelizedTransactionCounter, + conflictingButCachedTransactionCounter) + .orElse(null); + } + + if (transactionProcessingResult == null) { + return super.getTransactionProcessingResult( + preProcessingContext, + worldState, + blockUpdater, + privateMetadataUpdater, + blockHeader, + blobGasPrice, + miningBeneficiary, + transaction, + location, + blockHashLookup); + } else { + return transactionProcessingResult; + } + } + + static class ParallelizedPreProcessingContext implements PreprocessingContext { + final ParallelizedConcurrentTransactionProcessor parallelizedConcurrentTransactionProcessor; + + public ParallelizedPreProcessingContext( + final ParallelizedConcurrentTransactionProcessor + parallelizedConcurrentTransactionProcessor) { + this.parallelizedConcurrentTransactionProcessor = parallelizedConcurrentTransactionProcessor; + } + + public ParallelizedConcurrentTransactionProcessor + getParallelizedConcurrentTransactionProcessor() { + return parallelizedConcurrentTransactionProcessor; + } + } + + public static class ParallelBlockProcessorBuilder + implements ProtocolSpecBuilder.BlockProcessorBuilder { + + final MetricsSystem metricsSystem; + + public ParallelBlockProcessorBuilder(final MetricsSystem metricsSystem) { + this.metricsSystem = metricsSystem; + } + + @Override + public BlockProcessor apply( + final MainnetTransactionProcessor transactionProcessor, + final TransactionReceiptFactory transactionReceiptFactory, + final Wei blockReward, + final MiningBeneficiaryCalculator miningBeneficiaryCalculator, + final boolean skipZeroBlockRewards, + final ProtocolSchedule protocolSchedule) { + return new MainnetParallelBlockProcessor( + transactionProcessor, + transactionReceiptFactory, + blockReward, + miningBeneficiaryCalculator, + skipZeroBlockRewards, + protocolSchedule, + metricsSystem); + } + } +} 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 new file mode 100644 index 0000000000..c6beaa2f40 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java @@ -0,0 +1,268 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.parallelization; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; +import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.NoopBonsaiCachedMerkleTrieLoader; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; +import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldState; +import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; +import org.hyperledger.besu.evm.operation.BlockHashOperation; +import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.worldstate.WorldView; +import org.hyperledger.besu.plugin.services.metrics.Counter; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Optimizes transaction processing by executing transactions in parallel within a given block. + * Transactions are executed optimistically in a non-blocking manner. After execution, the class + * checks for potential conflicts among transactions to ensure data integrity before applying the + * results to the world state. + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +public class ParallelizedConcurrentTransactionProcessor { + + private static final int NCPU = Runtime.getRuntime().availableProcessors(); + private static final Executor executor = Executors.newFixedThreadPool(NCPU); + + private final MainnetTransactionProcessor transactionProcessor; + + private final TransactionCollisionDetector transactionCollisionDetector; + + private final Map + parallelizedTransactionContextByLocation = new ConcurrentHashMap<>(); + + /** + * Constructs a PreloadConcurrentTransactionProcessor with a specified transaction processor. This + * processor is responsible for the individual processing of transactions. + * + * @param transactionProcessor The transaction processor for processing individual transactions. + */ + public ParallelizedConcurrentTransactionProcessor( + final MainnetTransactionProcessor transactionProcessor) { + this.transactionProcessor = transactionProcessor; + this.transactionCollisionDetector = new TransactionCollisionDetector(); + } + + @VisibleForTesting + public ParallelizedConcurrentTransactionProcessor( + final MainnetTransactionProcessor transactionProcessor, + final TransactionCollisionDetector transactionCollisionDetector) { + this.transactionProcessor = transactionProcessor; + this.transactionCollisionDetector = transactionCollisionDetector; + } + + /** + * Initiates the parallel and optimistic execution of transactions within a block by creating a + * copy of the world state for each transaction. This method processes transactions in a + * non-blocking manner. Transactions are executed against their respective copies of the world + * state, ensuring that the original world state passed as a parameter remains unmodified during + * this process. + * + * @param worldState Mutable world state intended for applying transaction results. This world + * state is not modified directly; instead, copies are made for transaction execution. + * @param blockHeader Header of the current block containing the transactions. + * @param transactions List of transactions to be processed. + * @param miningBeneficiary Address of the beneficiary to receive mining rewards. + * @param blockHashLookup Function for block hash lookup. + * @param blobGasPrice Gas price for blob transactions. + * @param privateMetadataUpdater Updater for private transaction metadata. + */ + public void runAsyncBlock( + final MutableWorldState worldState, + final BlockHeader blockHeader, + final List transactions, + final Address miningBeneficiary, + final BlockHashOperation.BlockHashLookup blockHashLookup, + final Wei blobGasPrice, + final PrivateMetadataUpdater privateMetadataUpdater) { + for (int i = 0; i < transactions.size(); i++) { + final Transaction transaction = transactions.get(i); + final int transactionLocation = i; + /* + * All transactions are executed in the background by copying the world state of the block on which the transactions need to be executed, ensuring that each one has its own accumulator. + */ + CompletableFuture.runAsync( + () -> + runTransaction( + worldState, + blockHeader, + transactionLocation, + transaction, + miningBeneficiary, + blockHashLookup, + blobGasPrice, + privateMetadataUpdater), + executor); + } + } + + @VisibleForTesting + public void runTransaction( + final MutableWorldState worldState, + final BlockHeader blockHeader, + final int transactionLocation, + final Transaction transaction, + final Address miningBeneficiary, + final BlockHashOperation.BlockHashLookup blockHashLookup, + final Wei blobGasPrice, + final PrivateMetadataUpdater privateMetadataUpdater) { + try (final DiffBasedWorldState roundWorldState = + new BonsaiWorldState( + (BonsaiWorldState) worldState, new NoopBonsaiCachedMerkleTrieLoader())) { + roundWorldState.freeze(); // make the clone frozen + final ParallelizedTransactionContext.Builder contextBuilder = + new ParallelizedTransactionContext.Builder(); + final DiffBasedWorldStateUpdateAccumulator roundWorldStateUpdater = + (DiffBasedWorldStateUpdateAccumulator) roundWorldState.updater(); + final TransactionProcessingResult result = + transactionProcessor.processTransaction( + roundWorldStateUpdater, + blockHeader, + transaction, + miningBeneficiary, + new OperationTracer() { + @Override + public void traceBeforeRewardTransaction( + final WorldView worldView, + final org.hyperledger.besu.datatypes.Transaction tx, + final Wei miningReward) { + /* + * This part checks if the mining beneficiary's account was accessed before increasing its balance for rewards. + * Indeed, if the transaction has interacted with the address to read or modify it, + * it means that the value is necessary for the proper execution of the transaction and will therefore be considered in collision detection. + * If this is not the case, we can ignore this address during conflict detection. + */ + if (transactionCollisionDetector + .getAddressesTouchedByTransaction( + transaction, Optional.of(roundWorldStateUpdater)) + .contains(miningBeneficiary)) { + contextBuilder.isMiningBeneficiaryTouchedPreRewardByTransaction(true); + } + contextBuilder.miningBeneficiaryReward(miningReward); + } + }, + blockHashLookup, + true, + TransactionValidationParams.processingBlock(), + privateMetadataUpdater, + blobGasPrice); + + // commit the accumulator in order to apply all the modifications + roundWorldState.getAccumulator().commit(); + + contextBuilder + .transactionAccumulator(roundWorldState.getAccumulator()) + .transactionProcessingResult(result); + + final ParallelizedTransactionContext parallelizedTransactionContext = contextBuilder.build(); + if (!parallelizedTransactionContext.isMiningBeneficiaryTouchedPreRewardByTransaction()) { + /* + * If the address of the mining beneficiary has been touched only for adding rewards, + * we remove it from the accumulator to avoid a false positive collision. + * The balance will be increased during the sequential processing. + */ + roundWorldStateUpdater.getAccountsToUpdate().remove(miningBeneficiary); + } + parallelizedTransactionContextByLocation.put( + transactionLocation, parallelizedTransactionContext); + } + } + + /** + * Applies the results of parallelized transactions to the world state after checking for + * conflicts. + * + *

If a transaction was executed optimistically without any detected conflicts, its result is + * directly applied to the world state. If there is a conflict, this method does not apply the + * transaction's modifications directly to the world state. Instead, it caches the data read from + * the database during the transaction's execution. This cached data is then used to optimize the + * replay of the transaction by reducing the need for additional reads from the disk, thereby + * making the replay process faster. This approach ensures that the integrity of the world state + * is maintained while optimizing the performance of transaction processing. + * + * @param worldState Mutable world state intended for applying transaction results. + * @param miningBeneficiary Address of the beneficiary for mining rewards. + * @param transaction Transaction for which the result is to be applied. + * @param transactionLocation Index of the transaction within the block. + * @param confirmedParallelizedTransactionCounter Metric counter for confirmed parallelized + * transactions + * @param conflictingButCachedTransactionCounter Metric counter for conflicting but cached + * transactions + * @return Optional containing the transaction processing result if applied, or empty if the + * transaction needs to be replayed due to a conflict. + */ + public Optional applyParallelizedTransactionResult( + final MutableWorldState worldState, + final Address miningBeneficiary, + final Transaction transaction, + final int transactionLocation, + final Optional confirmedParallelizedTransactionCounter, + final Optional conflictingButCachedTransactionCounter) { + final DiffBasedWorldState diffBasedWorldState = (DiffBasedWorldState) worldState; + final DiffBasedWorldStateUpdateAccumulator blockAccumulator = + (DiffBasedWorldStateUpdateAccumulator) diffBasedWorldState.updater(); + final ParallelizedTransactionContext parallelizedTransactionContext = + parallelizedTransactionContextByLocation.remove(transactionLocation); + /* + * If `parallelizedTransactionContext` is not null, it means that the transaction had time to complete in the background. + */ + if (parallelizedTransactionContext != null) { + final DiffBasedWorldStateUpdateAccumulator transactionAccumulator = + parallelizedTransactionContext.transactionAccumulator(); + final TransactionProcessingResult transactionProcessingResult = + parallelizedTransactionContext.transactionProcessingResult(); + final boolean hasCollision = + transactionCollisionDetector.hasCollision( + transaction, miningBeneficiary, parallelizedTransactionContext, blockAccumulator); + if (transactionProcessingResult.isSuccessful() && !hasCollision) { + blockAccumulator + .getOrCreate(miningBeneficiary) + .incrementBalance(parallelizedTransactionContext.miningBeneficiaryReward()); + + blockAccumulator.importStateChangesFromSource(transactionAccumulator); + + if (confirmedParallelizedTransactionCounter.isPresent()) + confirmedParallelizedTransactionCounter.get().inc(); + return Optional.of(transactionProcessingResult); + } else { + blockAccumulator.importPriorStateFromSource(transactionAccumulator); + if (conflictingButCachedTransactionCounter.isPresent()) + conflictingButCachedTransactionCounter.get().inc(); + // If there is a conflict, we return an empty result to signal the block processor to + // re-execute the transaction. + return Optional.empty(); + } + } + return Optional.empty(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedTransactionContext.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedTransactionContext.java new file mode 100644 index 0000000000..30305ce00b --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedTransactionContext.java @@ -0,0 +1,133 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.parallelization; + +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; +import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; + +import java.util.Objects; + +public final class ParallelizedTransactionContext { + private final DiffBasedWorldStateUpdateAccumulator transactionAccumulator; + private final TransactionProcessingResult transactionProcessingResult; + private final boolean isMiningBeneficiaryTouchedPreRewardByTransaction; + private final Wei miningBeneficiaryReward; + + public ParallelizedTransactionContext( + final DiffBasedWorldStateUpdateAccumulator transactionAccumulator, + final TransactionProcessingResult transactionProcessingResult, + final boolean isMiningBeneficiaryTouchedPreRewardByTransaction, + final Wei miningBeneficiaryReward) { + this.transactionAccumulator = transactionAccumulator; + this.transactionProcessingResult = transactionProcessingResult; + this.isMiningBeneficiaryTouchedPreRewardByTransaction = + isMiningBeneficiaryTouchedPreRewardByTransaction; + this.miningBeneficiaryReward = miningBeneficiaryReward; + } + + public DiffBasedWorldStateUpdateAccumulator transactionAccumulator() { + return transactionAccumulator; + } + + public TransactionProcessingResult transactionProcessingResult() { + return transactionProcessingResult; + } + + public boolean isMiningBeneficiaryTouchedPreRewardByTransaction() { + return isMiningBeneficiaryTouchedPreRewardByTransaction; + } + + public Wei miningBeneficiaryReward() { + return miningBeneficiaryReward; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (ParallelizedTransactionContext) obj; + return Objects.equals(this.transactionAccumulator, that.transactionAccumulator) + && Objects.equals(this.transactionProcessingResult, that.transactionProcessingResult) + && this.isMiningBeneficiaryTouchedPreRewardByTransaction + == that.isMiningBeneficiaryTouchedPreRewardByTransaction + && Objects.equals(this.miningBeneficiaryReward, that.miningBeneficiaryReward); + } + + @Override + public int hashCode() { + return Objects.hash( + transactionAccumulator, + transactionProcessingResult, + isMiningBeneficiaryTouchedPreRewardByTransaction, + miningBeneficiaryReward); + } + + @Override + public String toString() { + return "ParallelizedTransactionContext[" + + "transactionAccumulator=" + + transactionAccumulator + + ", " + + "transactionProcessingResult=" + + transactionProcessingResult + + ", " + + "isMiningBeneficiaryTouchedPreRewardByTransaction=" + + isMiningBeneficiaryTouchedPreRewardByTransaction + + ", " + + "miningBeneficiaryReward=" + + miningBeneficiaryReward + + ']'; + } + + public static class Builder { + private DiffBasedWorldStateUpdateAccumulator transactionAccumulator; + private TransactionProcessingResult transactionProcessingResult; + private boolean isMiningBeneficiaryTouchedPreRewardByTransaction; + private Wei miningBeneficiaryReward = Wei.ZERO; + + public Builder transactionAccumulator( + final DiffBasedWorldStateUpdateAccumulator transactionAccumulator) { + this.transactionAccumulator = transactionAccumulator; + return this; + } + + public Builder transactionProcessingResult( + final TransactionProcessingResult transactionProcessingResult) { + this.transactionProcessingResult = transactionProcessingResult; + return this; + } + + public Builder isMiningBeneficiaryTouchedPreRewardByTransaction( + final boolean isMiningBeneficiaryTouchedPreRewardByTransaction) { + this.isMiningBeneficiaryTouchedPreRewardByTransaction = + isMiningBeneficiaryTouchedPreRewardByTransaction; + return this; + } + + public Builder miningBeneficiaryReward(final Wei miningBeneficiaryReward) { + this.miningBeneficiaryReward = miningBeneficiaryReward; + return this; + } + + public ParallelizedTransactionContext build() { + return new ParallelizedTransactionContext( + transactionAccumulator, + transactionProcessingResult, + isMiningBeneficiaryTouchedPreRewardByTransaction, + miningBeneficiaryReward); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/TransactionCollisionDetector.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/TransactionCollisionDetector.java new file mode 100644 index 0000000000..4120c0edb1 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/TransactionCollisionDetector.java @@ -0,0 +1,114 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.parallelization; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Optional; +import java.util.Set; + +public class TransactionCollisionDetector { + + /** + * Determines if a transaction has a collision based on the addresses it touches. A collision + * occurs if the transaction touches the mining beneficiary address or if there are common + * addresses touched by both the transaction and other transactions within the same block. + * + * @param transaction The transaction to check for collisions. + * @param miningBeneficiary The address of the mining beneficiary. + * @param parallelizedTransactionContext The context containing the accumulator for the + * transaction. + * @param blockAccumulator The accumulator for the block. + * @return true if there is a collision; false otherwise. + */ + public boolean hasCollision( + final Transaction transaction, + final Address miningBeneficiary, + final ParallelizedTransactionContext parallelizedTransactionContext, + final DiffBasedWorldStateUpdateAccumulator blockAccumulator) { + final Set

addressesTouchedByTransaction = + getAddressesTouchedByTransaction( + transaction, Optional.of(parallelizedTransactionContext.transactionAccumulator())); + if (addressesTouchedByTransaction.contains(miningBeneficiary)) { + return true; + } + final Set
addressesTouchedByBlock = + getAddressesTouchedByBlock(Optional.of(blockAccumulator)); + final Iterator
it = addressesTouchedByTransaction.iterator(); + boolean commonAddressFound = false; + while (it.hasNext() && !commonAddressFound) { + if (addressesTouchedByBlock.contains(it.next())) { + commonAddressFound = true; + } + } + return commonAddressFound; + } + + /** + * Retrieves the set of addresses that were touched by a transaction. This includes the sender and + * recipient of the transaction, as well as any addresses that were read from or written to by the + * transaction's execution. + * + * @param transaction The transaction to analyze. + * @param accumulator An optional accumulator containing state changes made by the transaction. + * @return A set of addresses touched by the transaction. + */ + public Set
getAddressesTouchedByTransaction( + final Transaction transaction, + final Optional> accumulator) { + HashSet
addresses = new HashSet<>(); + addresses.add(transaction.getSender()); + if (transaction.getTo().isPresent()) { + addresses.add(transaction.getTo().get()); + } + accumulator.ifPresent( + diffBasedWorldStateUpdateAccumulator -> { + diffBasedWorldStateUpdateAccumulator + .getAccountsToUpdate() + .forEach((address, diffBasedValue) -> addresses.add(address)); + addresses.addAll(diffBasedWorldStateUpdateAccumulator.getDeletedAccountAddresses()); + }); + return addresses; + } + + /** + * Retrieves the set of addresses that were touched by all transactions within a block. This + * method filters out addresses that were only read and not modified. + * + * @param accumulator An optional accumulator containing state changes made by the block. + * @return A set of addresses that were modified by the block's transactions. + */ + private Set
getAddressesTouchedByBlock( + final Optional> accumulator) { + HashSet
addresses = new HashSet<>(); + accumulator.ifPresent( + diffBasedWorldStateUpdateAccumulator -> { + diffBasedWorldStateUpdateAccumulator + .getAccountsToUpdate() + .forEach( + (address, diffBasedValue) -> { + if (!diffBasedValue.isUnchanged()) { + addresses.add(address); + } + }); + addresses.addAll(diffBasedWorldStateUpdateAccumulator.getDeletedAccountAddresses()); + }); + return addresses; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateMutableWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateMutableWorldStateUpdater.java index fc20db7f81..b4e8e28561 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateMutableWorldStateUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateMutableWorldStateUpdater.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.worldstate.AuthorizedCodeService; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.Collection; @@ -30,35 +31,39 @@ public class PrivateMutableWorldStateUpdater implements WorldUpdater { protected final WorldUpdater publicWorldUpdater; protected final WorldUpdater privateWorldUpdater; + private AuthorizedCodeService authorizedCodeService; public PrivateMutableWorldStateUpdater( final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) { this.publicWorldUpdater = publicWorldUpdater; this.privateWorldUpdater = privateWorldUpdater; + this.authorizedCodeService = new AuthorizedCodeService(); } @Override public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { - return privateWorldUpdater.createAccount(address, nonce, balance); + return authorizedCodeService.processMutableAccount( + this, privateWorldUpdater.createAccount(address, nonce, balance), address); } @Override public MutableAccount createAccount(final Address address) { - return privateWorldUpdater.createAccount(address); + return authorizedCodeService.processMutableAccount( + this, privateWorldUpdater.createAccount(address), address); } @Override public MutableAccount getAccount(final Address address) { final MutableAccount privateAccount = privateWorldUpdater.getAccount(address); if (privateAccount != null && !privateAccount.isEmpty()) { - return privateAccount; + return authorizedCodeService.processMutableAccount(this, privateAccount, address); } final MutableAccount publicAccount = publicWorldUpdater.getAccount(address); if (publicAccount != null && !publicAccount.isEmpty()) { publicAccount.becomeImmutable(); - return publicAccount; + return authorizedCodeService.processMutableAccount(this, publicAccount, address); } - return privateAccount; + return authorizedCodeService.processMutableAccount(this, privateAccount, address); } @Override @@ -104,4 +109,9 @@ public class PrivateMutableWorldStateUpdater implements WorldUpdater { public Optional parentUpdater() { return privateWorldUpdater.parentUpdater(); } + + @Override + public void setAuthorizedCodeService(final AuthorizedCodeService authorizedCodeService) { + this.authorizedCodeService = authorizedCodeService; + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/cache/NoopBonsaiCachedMerkleTrieLoader.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/cache/NoopBonsaiCachedMerkleTrieLoader.java new file mode 100644 index 0000000000..8f58e697fe --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/cache/NoopBonsaiCachedMerkleTrieLoader.java @@ -0,0 +1,44 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.StorageSlotKey; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; + +public class NoopBonsaiCachedMerkleTrieLoader extends BonsaiCachedMerkleTrieLoader { + + public NoopBonsaiCachedMerkleTrieLoader() { + super(new NoOpMetricsSystem()); + } + + @Override + public void preLoadAccount( + final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage, + final Hash worldStateRootHash, + final Address account) { + // noop + } + + @Override + public void preLoadStorageSlot( + final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage, + final Address account, + final StorageSlotKey slotKey) { + // noop + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java index ad838aa528..b62805c1fd 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/worldview/BonsaiWorldState.java @@ -76,6 +76,18 @@ public class BonsaiWorldState extends DiffBasedWorldState { diffBasedWorldStateConfig); } + public BonsaiWorldState( + final BonsaiWorldState worldState, + final BonsaiCachedMerkleTrieLoader cachedMerkleTrieLoader) { + this( + new BonsaiWorldStateLayerStorage(worldState.getWorldStateStorage()), + cachedMerkleTrieLoader, + worldState.cachedWorldStorageManager, + worldState.trieLogManager, + worldState.accumulator.getEvmConfiguration(), + new DiffBasedWorldStateConfig(worldState.worldStateConfig)); + } + public BonsaiWorldState( final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage, final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java index 857ec0b079..75b370c1cd 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedWorldStateProvider.java @@ -230,12 +230,12 @@ public abstract class DiffBasedWorldStateProvider implements WorldStateArchive { } catch (final Exception e) { // if we fail we must clean up the updater diffBasedUpdater.reset(); - LOG.debug( - "State rolling failed on " - + mutableState.getWorldStateStorage().getClass().getSimpleName() - + " for block hash " - + blockHash, - e); + LOG.atDebug() + .setMessage("State rolling failed on {} for block hash {}") + .addArgument(mutableState.getWorldStateStorage().getClass().getSimpleName()) + .addArgument(blockHash) + .addArgument(e) + .log(); return Optional.empty(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogPruner.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogPruner.java index 6f347be033..98bc4246eb 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogPruner.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/trielog/TrieLogPruner.java @@ -19,10 +19,19 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage; +import org.hyperledger.besu.metrics.BesuMetricCategory; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.metrics.Counter; import org.hyperledger.besu.plugin.services.trielogs.TrieLogEvent; import java.util.Comparator; import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.stream.Stream; @@ -37,6 +46,7 @@ import org.slf4j.LoggerFactory; public class TrieLogPruner implements TrieLogEvent.TrieLogObserver { private static final Logger LOG = LoggerFactory.getLogger(TrieLogPruner.class); + private static final int PRELOAD_TIMEOUT_IN_SECONDS = 30; private final int pruningLimit; private final int loadingLimit; @@ -45,6 +55,9 @@ public class TrieLogPruner implements TrieLogEvent.TrieLogObserver { private final Consumer executeAsync; private final long numBlocksToRetain; private final boolean requireFinalizedBlock; + private final Counter addedToPruneQueueCounter; + private final Counter prunedFromQueueCounter; + private final Counter prunedOrphanCounter; private final Multimap trieLogBlocksAndForksByDescendingBlockNumber = TreeMultimap.create(Comparator.reverseOrder(), Comparator.naturalOrder()); @@ -55,7 +68,8 @@ public class TrieLogPruner implements TrieLogEvent.TrieLogObserver { final Consumer executeAsync, final long numBlocksToRetain, final int pruningLimit, - final boolean requireFinalizedBlock) { + final boolean requireFinalizedBlock, + final MetricsSystem metricsSystem) { this.rootWorldStateStorage = rootWorldStateStorage; this.blockchain = blockchain; this.executeAsync = executeAsync; @@ -63,39 +77,95 @@ public class TrieLogPruner implements TrieLogEvent.TrieLogObserver { this.pruningLimit = pruningLimit; this.loadingLimit = pruningLimit; // same as pruningLimit for now this.requireFinalizedBlock = requireFinalizedBlock; + this.addedToPruneQueueCounter = + metricsSystem.createCounter( + BesuMetricCategory.PRUNER, + "trie_log_added_to_prune_queue", + "trie log added to prune queue"); + this.prunedFromQueueCounter = + metricsSystem.createCounter( + BesuMetricCategory.PRUNER, "trie_log_pruned_from_queue", "trie log pruned from queue"); + this.prunedOrphanCounter = + metricsSystem.createCounter( + BesuMetricCategory.PRUNER, "trie_log_pruned_orphan", "trie log pruned orphan"); } - public int initialize() { - return preloadQueue(); + public void initialize() { + preloadQueueWithTimeout(); } - private int preloadQueue() { + private void preloadQueueWithTimeout() { + LOG.atInfo() - .setMessage("Loading first {} trie logs from database...") + .setMessage("Attempting to load first {} trie logs from database...") .addArgument(loadingLimit) .log(); + + try (final ScheduledExecutorService preloadExecutor = Executors.newScheduledThreadPool(1)) { + + final AtomicBoolean timeoutOccurred = new AtomicBoolean(false); + final Runnable timeoutTask = + () -> { + timeoutOccurred.set(true); + LOG.atWarn() + .setMessage( + "Timeout occurred while loading and processing {} trie logs from database") + .addArgument(loadingLimit) + .log(); + }; + + final ScheduledFuture timeoutFuture = + preloadExecutor.schedule(timeoutTask, PRELOAD_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); + LOG.atInfo() + .setMessage( + "Trie log pruning will timeout after {} seconds. If this is timing out, consider using `besu storage trie-log prune` subcommand, see https://besu.hyperledger.org/public-networks/how-to/bonsai-limit-trie-logs") + .addArgument(PRELOAD_TIMEOUT_IN_SECONDS) + .log(); + + preloadQueue(timeoutOccurred, timeoutFuture); + } + } + + private void preloadQueue( + final AtomicBoolean timeoutOccurred, final ScheduledFuture timeoutFuture) { + try (final Stream trieLogKeys = rootWorldStateStorage.streamTrieLogKeys(loadingLimit)) { - final AtomicLong count = new AtomicLong(); + + final AtomicLong addToPruneQueueCount = new AtomicLong(); final AtomicLong orphansPruned = new AtomicLong(); trieLogKeys.forEach( blockHashAsBytes -> { + if (timeoutOccurred.get()) { + throw new RuntimeException( + new TimeoutException("Timeout occurred while preloading trie log prune queue")); + } final Hash blockHash = Hash.wrap(Bytes32.wrap(blockHashAsBytes)); final Optional header = blockchain.getBlockHeader(blockHash); if (header.isPresent()) { addToPruneQueue(header.get().getNumber(), blockHash); - count.getAndIncrement(); + addToPruneQueueCount.getAndIncrement(); } else { // prune orphaned blocks (sometimes created during block production) rootWorldStateStorage.pruneTrieLog(blockHash); orphansPruned.getAndIncrement(); + prunedOrphanCounter.inc(); } }); + + timeoutFuture.cancel(true); LOG.atDebug().log("Pruned {} orphaned trie logs from database...", orphansPruned.intValue()); - LOG.atInfo().log("Loaded {} trie logs from database", count); - return pruneFromQueue() + orphansPruned.intValue(); + LOG.atInfo().log( + "Added {} trie logs to prune queue. Commencing pruning of eligible trie logs...", + addToPruneQueueCount.intValue()); + int prunedCount = pruneFromQueue(); + LOG.atInfo().log("Pruned {} trie logs.", prunedCount); } catch (Exception e) { - LOG.error("Error loading trie logs from database, nothing pruned", e); - return 0; + if (e.getCause() != null && e.getCause() instanceof TimeoutException) { + int prunedCount = pruneFromQueue(); + LOG.atInfo().log("Operation timed out, but still pruned {} trie logs.", prunedCount); + } else { + LOG.error("Error loading trie logs from database, nothing pruned", e); + } } } @@ -106,6 +176,7 @@ public class TrieLogPruner implements TrieLogEvent.TrieLogObserver { .addArgument(blockHash) .log(); trieLogBlocksAndForksByDescendingBlockNumber.put(blockNumber, blockHash); + addedToPruneQueueCounter.inc(); } public synchronized int pruneFromQueue() { @@ -155,6 +226,7 @@ public class TrieLogPruner implements TrieLogEvent.TrieLogObserver { }); wasPruned.keySet().forEach(trieLogBlocksAndForksByDescendingBlockNumber::removeAll); + prunedFromQueueCounter.inc(wasPruned.size()); LOG.atTrace() .setMessage("pruned {} trie logs for blocks {}") 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 149cefbe54..0799ed3db2 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 @@ -90,7 +90,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator source) { + public void cloneFromUpdater(final DiffBasedWorldStateUpdateAccumulator source) { accountsToUpdate.putAll(source.getAccountsToUpdate()); codeToUpdate.putAll(source.codeToUpdate); storageToClear.addAll(source.storageToClear); @@ -100,6 +100,120 @@ public abstract class DiffBasedWorldStateUpdateAccumulator source) { + source + .getAccountsToUpdate() + .forEach( + (address, diffBasedValue) -> { + ACCOUNT copyPrior = + diffBasedValue.getPrior() != null + ? copyAccount(diffBasedValue.getPrior(), this, false) + : null; + ACCOUNT copyUpdated = + diffBasedValue.getUpdated() != null + ? copyAccount(diffBasedValue.getUpdated(), this, true) + : null; + accountsToUpdate.put(address, new DiffBasedValue<>(copyPrior, copyUpdated)); + }); + source + .getCodeToUpdate() + .forEach( + (address, diffBasedValue) -> { + codeToUpdate.put( + address, + new DiffBasedValue<>(diffBasedValue.getPrior(), diffBasedValue.getUpdated())); + }); + source + .getStorageToUpdate() + .forEach( + (address, slots) -> { + StorageConsumingMap> storageConsumingMap = + storageToUpdate.computeIfAbsent( + address, + k -> + new StorageConsumingMap<>( + address, new ConcurrentHashMap<>(), storagePreloader)); + slots.forEach( + (storageSlotKey, uInt256DiffBasedValue) -> { + storageConsumingMap.put( + storageSlotKey, + new DiffBasedValue<>( + uInt256DiffBasedValue.getPrior(), uInt256DiffBasedValue.getUpdated())); + }); + }); + storageToClear.addAll(source.storageToClear); + + this.isAccumulatorStateChanged = true; + } + + /** + * Imports unchanged state data from an external source into the current state. This method + * focuses on integrating state data from the specified source that has been read but not + * modified. + * + *

The method ensures that only new, unmodified data from the source is added to the current + * state. If a state data has already been read or modified in the current state, it will not be + * added again to avoid overwriting any existing modifications. + * + * @param source The source accumulator + */ + public void importPriorStateFromSource( + final DiffBasedWorldStateUpdateAccumulator source) { + + source + .getAccountsToUpdate() + .forEach( + (address, diffBasedValue) -> { + ACCOUNT copyPrior = + diffBasedValue.getPrior() != null + ? copyAccount(diffBasedValue.getPrior(), this, false) + : null; + ACCOUNT copyUpdated = + diffBasedValue.getPrior() != null + ? copyAccount(diffBasedValue.getPrior(), this, true) + : null; + accountsToUpdate.putIfAbsent(address, new DiffBasedValue<>(copyPrior, copyUpdated)); + }); + source + .getCodeToUpdate() + .forEach( + (address, diffBasedValue) -> { + codeToUpdate.putIfAbsent( + address, + new DiffBasedValue<>(diffBasedValue.getPrior(), diffBasedValue.getPrior())); + }); + source + .getStorageToUpdate() + .forEach( + (address, slots) -> { + StorageConsumingMap> storageConsumingMap = + storageToUpdate.computeIfAbsent( + address, + k -> + new StorageConsumingMap<>( + address, new ConcurrentHashMap<>(), storagePreloader)); + slots.forEach( + (storageSlotKey, uInt256DiffBasedValue) -> { + storageConsumingMap.putIfAbsent( + storageSlotKey, + new DiffBasedValue<>( + uInt256DiffBasedValue.getPrior(), uInt256DiffBasedValue.getPrior())); + }); + }); + this.isAccumulatorStateChanged = true; + } + protected Consumer> getAccountPreloader() { return accountPreloader; } @@ -108,7 +222,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator(diffBasedValue.getUpdated())); + return authorizedCodeService.processMutableAccount( + this, track(new UpdateTrackingAccount<>(diffBasedValue.getUpdated())), address); } else { throw new IllegalStateException("Cannot create an account when one already exists"); } @@ -153,7 +268,8 @@ public abstract class DiffBasedWorldStateUpdateAccumulator(newAccount)); + return authorizedCodeService.processMutableAccount( + this, track(new UpdateTrackingAccount<>(newAccount)), address); } @Override @@ -232,6 +348,7 @@ public abstract class DiffBasedWorldStateUpdateAccumulator accountValue = accountsToUpdate.computeIfAbsent( @@ -303,7 +420,6 @@ public abstract class DiffBasedWorldStateUpdateAccumulator updatedAccountValue = accountsToUpdate.get(updatedAddress); - final Map> pendingStorageUpdates = storageToUpdate.computeIfAbsent( updatedAddress, @@ -357,12 +473,6 @@ public abstract class DiffBasedWorldStateUpdateAccumulator eip1559Transaction(payload, to); case ACCESS_LIST -> accessListTransaction(payload, to); case BLOB -> blobTransaction(payload, to); + case SET_CODE -> null; // no default, all types accounted for. }; } diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java index 60b40c5f24..551f593e82 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java @@ -142,7 +142,9 @@ public class BlockchainSetupUtil { genesisConfigFile.getConfigOptions(), EvmConfiguration.DEFAULT, MiningParameters.newDefault(), - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } private static ProtocolContext mainnetProtocolContextProvider( diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java index 5aa00d0ed6..91ab41b636 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java @@ -146,7 +146,9 @@ public class ExecutionContextTestFixture { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); } if (blockchainKeyValueStorage == null) { diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ProtocolScheduleFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ProtocolScheduleFixture.java index a4c6556ea3..e94c6b49fd 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ProtocolScheduleFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ProtocolScheduleFixture.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.chain.BadBlockManager; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.io.IOException; @@ -39,7 +40,9 @@ public class ProtocolScheduleFixture { false, EvmConfiguration.DEFAULT, MiningParameters.newDefault(), - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); private static GenesisConfigOptions getMainnetConfigOptions() { // this method avoids reading all the alloc accounts when all we want is the "config" section diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java index d016b7f4e5..3f7049be39 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java @@ -92,6 +92,8 @@ public class TransactionTestFixture { builder.versionedHashes(versionedHashes.get()); } break; + case SET_CODE: + break; } to.ifPresent(builder::to); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java index d2d1fed8b7..d3a261803f 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java @@ -41,6 +41,7 @@ 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.blockhash.FrontierBlockHashProcessor; +import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.requests.RequestsValidatorCoordinator; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiWorldStateProvider; @@ -50,6 +51,7 @@ import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWo import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.services.exception.StorageException; @@ -80,6 +82,8 @@ class BlockImportExceptionHandlingTest { private final BlockBodyValidator blockBodyValidator = mock(BlockBodyValidator.class); private final ProtocolContext protocolContext = mock(ProtocolContext.class); private final ProtocolSpec protocolSpec = mock(ProtocolSpec.class); + private final GasCalculator gasCalculator = mock(GasCalculator.class); + private final FeeMarket feeMarket = mock(FeeMarket.class); protected final MutableBlockchain blockchain = mock(MutableBlockchain.class); private final StorageProvider storageProvider = new InMemoryKeyValueStorageProvider(); @@ -116,6 +120,8 @@ class BlockImportExceptionHandlingTest { when(protocolSpec.getRequestsValidatorCoordinator()) .thenReturn(RequestsValidatorCoordinator.empty()); when(protocolSpec.getBlockHashProcessor()).thenReturn(new FrontierBlockHashProcessor()); + when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator); + when(protocolSpec.getFeeMarket()).thenReturn(feeMarket); mainnetBlockValidator = new MainnetBlockValidator( blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoderTest.java new file mode 100644 index 0000000000..610b5906f5 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoderTest.java @@ -0,0 +1,120 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core.encoding; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.SetCodeAuthorization; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; + +import java.math.BigInteger; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; + +class SetCodeTransactionDecoderTest { + + @Test + void shouldDecodeInnerPayloadWithNonce() { + // "0xd80194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c105" + + final BytesValueRLPInput input = + new BytesValueRLPInput( + Bytes.fromHexString( + "0xf85b0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c18080a0840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5a03b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99"), + true); + final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input); + + assertThat(authorization.chainId()).isEqualTo(BigInteger.ONE); + assertThat(authorization.address()) + .isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56")); + assertThat(authorization.nonce().get()).isEqualTo(0L); + + final SECPSignature signature = authorization.signature(); + assertThat(signature.getRecId()).isEqualTo((byte) 0); + assertThat(signature.getR().toString(16)) + .isEqualTo("840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5"); + assertThat(signature.getS().toString(16)) + .isEqualTo("3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99"); + } + + @Test + void shouldDecodeInnerPayloadWithoutNonce() { + // "0xd70194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5" + + final BytesValueRLPInput input = + new BytesValueRLPInput( + Bytes.fromHexString( + "0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148a025b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031"), + true); + final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input); + + assertThat(authorization.chainId()).isEqualTo(BigInteger.ONE); + assertThat(authorization.address()) + .isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56")); + assertThat(authorization.nonce()).isEmpty(); + + final SECPSignature signature = authorization.signature(); + assertThat(signature.getRecId()).isEqualTo((byte) 1); + assertThat(signature.getR().toString(16)) + .isEqualTo("dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148"); + assertThat(signature.getS().toString(16)) + .isEqualTo("25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031"); + } + + @Test + void shouldThrowInnerPayloadWithMultipleNonces() { + // "d90194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c20107" + + final BytesValueRLPInput input = + new BytesValueRLPInput( + Bytes.fromHexString( + "0xf85c0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c2010201a0401b5d4ebe88306448115d1a46a30e5ad1136f2818b4ebb0733d9c4efffd135aa0753ff1dbce6db504ecb9635a64d8c4506ff887e2d2a0d2b7175baf94c849eccc"), + true); + + assertThrows( + IllegalArgumentException.class, + () -> { + SetCodeTransactionDecoder.decodeInnerPayload(input); + }); + } + + @Test + void shouldDecodeInnerPayloadWithoutNonceAndChainIdZero() { + // "d70094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5" + + final BytesValueRLPInput input = + new BytesValueRLPInput( + Bytes.fromHexString( + "0xf85a0094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0025c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2a03c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df"), + true); + final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input); + + assertThat(authorization.chainId()).isEqualTo(BigInteger.ZERO); + assertThat(authorization.address()) + .isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56")); + assertThat(authorization.nonce().isEmpty()).isTrue(); + + final SECPSignature signature = authorization.signature(); + assertThat(signature.getRecId()).isEqualTo((byte) 1); + assertThat(signature.getR().toString(16)) + .isEqualTo("25c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2"); + assertThat(signature.getS().toString(16)) + .isEqualTo("3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df"); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoderTest.java new file mode 100644 index 0000000000..89211f4ba3 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoderTest.java @@ -0,0 +1,122 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core.encoding; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.ethereum.core.SetCodeAuthorization; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; + +import java.math.BigInteger; +import java.util.Optional; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SetCodeTransactionEncoderTest { + private static final Supplier SIGNATURE_ALGORITHM = + Suppliers.memoize(SignatureAlgorithmFactory::getInstance); + + BytesValueRLPOutput output; + + @BeforeEach + void setUp() { + output = new BytesValueRLPOutput(); + } + + @Test + void shouldEncodeSingleSetCodeWithNonce() { + // "0xd80194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c105" + + final SetCodeAuthorization authorization = + new SetCodeAuthorization( + BigInteger.ONE, + Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"), + Optional.of(0L), + SIGNATURE_ALGORITHM + .get() + .createSignature( + new BigInteger( + "840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5", 16), + new BigInteger( + "3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99", 16), + (byte) 0)); + + SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output); + + assertThat(output.encoded()) + .isEqualTo( + Bytes.fromHexString( + "0xf85b0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c18080a0840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5a03b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99")); + } + + @Test + void shouldEncodeSingleSetCodeWithoutNonce() { + // "0xd70194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5" + + final SetCodeAuthorization authorization = + new SetCodeAuthorization( + BigInteger.ONE, + Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"), + Optional.empty(), + SIGNATURE_ALGORITHM + .get() + .createSignature( + new BigInteger( + "dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148", 16), + new BigInteger( + "25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031", 16), + (byte) 1)); + + SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output); + + assertThat(output.encoded()) + .isEqualTo( + Bytes.fromHexString( + "0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148a025b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031")); + } + + @Test + void shouldEncodeSingleSetCodeWithoutNonceAndChainIdZero() { + // "d70094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5" + + final SetCodeAuthorization authorization = + new SetCodeAuthorization( + BigInteger.ZERO, + Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"), + Optional.empty(), + SIGNATURE_ALGORITHM + .get() + .createSignature( + new BigInteger( + "25c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2", 16), + new BigInteger( + "3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df", 16), + (byte) 1)); + + SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output); + + assertThat(output.encoded()) + .isEqualTo( + Bytes.fromHexString( + "0xf85a8094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0025c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2a03c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df")); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedProtocolScheduleTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedProtocolScheduleTest.java index adaf4c476b..7969ab6824 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedProtocolScheduleTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedProtocolScheduleTest.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.junit.jupiter.api.Test; @@ -36,7 +37,9 @@ public class FixedProtocolScheduleTest { GenesisConfigFile.fromResource("/dev.json").getConfigOptions(), EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/forkid/ForkIdBackwardCompatibilityTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/forkid/ForkIdBackwardCompatibilityTest.java index 84c24bc9a2..df97386e9f 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/forkid/ForkIdBackwardCompatibilityTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/forkid/ForkIdBackwardCompatibilityTest.java @@ -18,6 +18,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.ethereum.forkid.ForkIdTestUtil.GenesisHash; import static org.hyperledger.besu.ethereum.forkid.ForkIdTestUtil.mockBlockchain; +import org.hyperledger.besu.ethereum.chain.Blockchain; + import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -77,6 +79,13 @@ public class ForkIdBackwardCompatibilityTest { 8L, Arrays.asList(0L, 0L, 4L, 5L, 6L), true, + null), + Arguments.of( + "no forks and legacyEth64=true", + GenesisHash.PRIVATE, + 8L, + Collections.emptyList(), + true, null)); } @@ -90,13 +99,11 @@ public class ForkIdBackwardCompatibilityTest { final boolean legacyEth64, final ForkId wantForkId) { LOG.info("Running test case {}", name); + final Blockchain blockchain = mockBlockchain(genesisHash, head, 0); final ForkIdManager forkIdManager = - new ForkIdManager( - mockBlockchain(genesisHash, head, 0), forks, Collections.emptyList(), legacyEth64); + new ForkIdManager(blockchain, forks, Collections.emptyList(), legacyEth64); final ForkId legacyForkId = - legacyEth64 - ? new LegacyForkIdManager(mockBlockchain(genesisHash, head, 0), forks).getLatestForkId() - : null; + legacyEth64 ? new LegacyForkIdManager(blockchain, forks).getLatestForkId() : null; assertThat(forkIdManager.getForkIdForChainHead()) .isEqualTo(legacyEth64 ? legacyForkId : wantForkId); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/forkid/LegacyForkIdManager.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/forkid/LegacyForkIdManager.java index 325fd41c81..bc9196e6d5 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/forkid/LegacyForkIdManager.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/forkid/LegacyForkIdManager.java @@ -20,7 +20,6 @@ import org.hyperledger.besu.ethereum.rlp.RLPInput; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import java.util.zip.CRC32; import org.apache.tuweni.bytes.Bytes; @@ -30,12 +29,13 @@ public class LegacyForkIdManager { private final Hash genesisHash; private final List forks; private List forkAndHashList; + private CRC32 crc; + private Bytes genesisHashCrc; public LegacyForkIdManager(final Blockchain blockchain, final List forks) { this.genesisHash = blockchain.getGenesisBlock().getHash(); // de-dupe and sanitize forks - this.forks = - forks.stream().filter(fork -> fork > 0).distinct().collect(Collectors.toUnmodifiableList()); + this.forks = forks.stream().filter(fork -> fork > 0).distinct().toList(); createForkIds(); } @@ -44,10 +44,10 @@ public class LegacyForkIdManager { } public ForkId getLatestForkId() { - if (forkAndHashList.size() > 0) { - return forkAndHashList.get(forkAndHashList.size() - 1); + if (!forkAndHashList.isEmpty()) { + return forkAndHashList.getLast(); } - return null; + return new ForkId(genesisHashCrc, 0); } public static ForkId readFrom(final RLPInput in) { @@ -59,8 +59,9 @@ public class LegacyForkIdManager { } private void createForkIds() { - final CRC32 crc = new CRC32(); + crc = new CRC32(); crc.update(genesisHash.toArray()); + genesisHashCrc = getCurrentCrcHash(crc); final List forkHashes = new ArrayList<>(List.of(getCurrentCrcHash(crc))); for (final Long fork : forks) { updateCrc(crc, fork); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolScheduleTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolScheduleTest.java index 9e4522912d..2d5dd2cee3 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolScheduleTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolScheduleTest.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; import java.util.Optional; @@ -61,7 +62,9 @@ public class DefaultProtocolScheduleTest { isRevertReasonEnabled, evmConfiguration, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } @Test diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolScheduleTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolScheduleTest.java index fa8cda5cff..c63a9b5ccb 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolScheduleTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolScheduleTest.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; @@ -73,7 +74,9 @@ public class MainnetProtocolScheduleTest { GenesisConfigFile.fromConfig("{}").getConfigOptions(), EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); Assertions.assertThat(sched.getByBlockHeader(blockHeader(1L)).getName()).isEqualTo("Frontier"); Assertions.assertThat(sched.getByBlockHeader(blockHeader(Long.MAX_VALUE)).getName()) .isEqualTo("Frontier"); @@ -88,7 +91,9 @@ public class MainnetProtocolScheduleTest { GenesisConfigFile.fromConfig(json).getConfigOptions(), EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); Assertions.assertThat(sched.getByBlockHeader(blockHeader(1)).getName()).isEqualTo("Frontier"); Assertions.assertThat(sched.getByBlockHeader(blockHeader(2)).getName()).isEqualTo("Homestead"); Assertions.assertThat(sched.getByBlockHeader(blockHeader(3)).getName()) @@ -120,7 +125,9 @@ public class MainnetProtocolScheduleTest { GenesisConfigFile.fromConfig(json).getConfigOptions(), EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager())); + new BadBlockManager(), + false, + new NoOpMetricsSystem())); } private BlockHeader blockHeader(final long number) { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java index d541cbd99a..b2f5718dad 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java @@ -39,6 +39,7 @@ import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldView; +import java.math.BigInteger; import java.util.List; import java.util.Optional; import java.util.Set; @@ -87,7 +88,8 @@ class MainnetTransactionProcessorTest { warmCoinbase, MAX_STACK_SIZE, FeeMarket.legacy(), - CoinbaseFeePriceCalculator.frontier()); + CoinbaseFeePriceCalculator.frontier(), + new AuthorityProcessor(Optional.of(BigInteger.ONE))); } @Test diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java index c7be26039e..0e1011de76 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.core.MilestoneStreamingProtocolSchedule; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; import java.util.OptionalLong; @@ -61,7 +62,9 @@ class ProtocolScheduleBuilderTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } @Test @@ -216,7 +219,9 @@ class ProtocolScheduleBuilderTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); return new MilestoneStreamingProtocolSchedule( (DefaultProtocolSchedule) builder.createProtocolSchedule()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessorTest.java new file mode 100644 index 0000000000..bba9184356 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessorTest.java @@ -0,0 +1,213 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.parallelization; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; +import org.hyperledger.besu.ethereum.mainnet.ValidationResult; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; +import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; +import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.NoOpBonsaiCachedWorldStorageManager; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.NoopBonsaiCachedMerkleTrieLoader; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; +import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.NoOpTrieLogManager; +import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldStateConfig; +import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; +import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; +import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.evm.operation.BlockHashOperation; +import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; + +import java.util.Collections; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class ParallelizedConcurrentTransactionProcessorTest { + + @Mock private MainnetTransactionProcessor transactionProcessor; + @Mock private BlockHeader blockHeader; + @Mock private Transaction transaction; + @Mock private PrivateMetadataUpdater privateMetadataUpdater; + @Mock private TransactionCollisionDetector transactionCollisionDetector; + + private BonsaiWorldState worldState; + + private ParallelizedConcurrentTransactionProcessor processor; + + @BeforeEach + void setUp() { + processor = + new ParallelizedConcurrentTransactionProcessor( + transactionProcessor, transactionCollisionDetector); + final BonsaiWorldStateKeyValueStorage bonsaiWorldStateKeyValueStorage = + new BonsaiWorldStateKeyValueStorage( + new InMemoryKeyValueStorageProvider(), + new NoOpMetricsSystem(), + DataStorageConfiguration.DEFAULT_BONSAI_CONFIG); + worldState = + new BonsaiWorldState( + bonsaiWorldStateKeyValueStorage, + new NoopBonsaiCachedMerkleTrieLoader(), + new NoOpBonsaiCachedWorldStorageManager(bonsaiWorldStateKeyValueStorage), + new NoOpTrieLogManager(), + EvmConfiguration.DEFAULT, + new DiffBasedWorldStateConfig()); + when(transactionCollisionDetector.hasCollision(any(), any(), any(), any())).thenReturn(false); + } + + @Test + void testRunTransaction() { + Address miningBeneficiary = Address.fromHexString("0x1"); + Wei blobGasPrice = Wei.ZERO; + + Mockito.when( + transactionProcessor.processTransaction( + any(), any(), any(), any(), any(), any(), anyBoolean(), any(), any(), any())) + .thenReturn( + TransactionProcessingResult.successful( + Collections.emptyList(), 0, 0, Bytes.EMPTY, ValidationResult.valid())); + + processor.runTransaction( + worldState, + blockHeader, + 0, + transaction, + miningBeneficiary, + (blockNumber) -> Hash.EMPTY, + blobGasPrice, + privateMetadataUpdater); + + verify(transactionProcessor, times(1)) + .processTransaction( + any(DiffBasedWorldStateUpdateAccumulator.class), + eq(blockHeader), + eq(transaction), + eq(miningBeneficiary), + any(OperationTracer.class), + any(BlockHashOperation.BlockHashLookup.class), + eq(true), + eq(TransactionValidationParams.processingBlock()), + eq(privateMetadataUpdater), + eq(blobGasPrice)); + + assertTrue( + processor + .applyParallelizedTransactionResult( + worldState, miningBeneficiary, transaction, 0, Optional.empty(), Optional.empty()) + .isPresent(), + "Expected the transaction context to be stored"); + } + + @Test + void testRunTransactionWithFailure() { + Address miningBeneficiary = Address.fromHexString("0x1"); + Wei blobGasPrice = Wei.ZERO; + + when(transactionProcessor.processTransaction( + any(), any(), any(), any(), any(), any(), anyBoolean(), any(), any(), any())) + .thenReturn( + TransactionProcessingResult.failed( + 0, + 0, + ValidationResult.invalid( + TransactionInvalidReason.BLOB_GAS_PRICE_BELOW_CURRENT_BLOB_BASE_FEE), + Optional.of(Bytes.EMPTY))); + + processor.runTransaction( + worldState, + blockHeader, + 0, + transaction, + miningBeneficiary, + (blockNumber) -> Hash.EMPTY, + blobGasPrice, + privateMetadataUpdater); + + Optional result = + processor.applyParallelizedTransactionResult( + worldState, miningBeneficiary, transaction, 0, Optional.empty(), Optional.empty()); + assertTrue(result.isEmpty(), "Expected the transaction result to indicate a failure"); + } + + @Test + void testRunTransactionWithConflict() { + + Address miningBeneficiary = Address.fromHexString("0x1"); + Wei blobGasPrice = Wei.ZERO; + + Mockito.when( + transactionProcessor.processTransaction( + any(), any(), any(), any(), any(), any(), anyBoolean(), any(), any(), any())) + .thenReturn( + TransactionProcessingResult.successful( + Collections.emptyList(), 0, 0, Bytes.EMPTY, ValidationResult.valid())); + + processor.runTransaction( + worldState, + blockHeader, + 0, + transaction, + miningBeneficiary, + (blockNumber) -> Hash.EMPTY, + blobGasPrice, + privateMetadataUpdater); + + verify(transactionProcessor, times(1)) + .processTransaction( + any(DiffBasedWorldStateUpdateAccumulator.class), + eq(blockHeader), + eq(transaction), + eq(miningBeneficiary), + any(OperationTracer.class), + any(BlockHashOperation.BlockHashLookup.class), + eq(true), + eq(TransactionValidationParams.processingBlock()), + eq(privateMetadataUpdater), + eq(blobGasPrice)); + + // simulate a conflict + when(transactionCollisionDetector.hasCollision(any(), any(), any(), any())).thenReturn(true); + + Optional result = + processor.applyParallelizedTransactionResult( + worldState, miningBeneficiary, transaction, 0, Optional.empty(), Optional.empty()); + assertTrue(result.isEmpty(), "Expected no transaction result to be applied due to conflict"); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/TransactionCollisionDetectorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/TransactionCollisionDetectorTest.java new file mode 100644 index 0000000000..0cd1bad3c5 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/TransactionCollisionDetectorTest.java @@ -0,0 +1,290 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet.parallelization; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiAccount; +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.trie.diffbased.common.DiffBasedValue; +import org.hyperledger.besu.evm.internal.EvmConfiguration; + +import java.math.BigInteger; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class TransactionCollisionDetectorTest { + + private TransactionCollisionDetector collisionDetector; + @Mock BonsaiWorldState worldState; + BonsaiWorldStateUpdateAccumulator bonsaiUpdater; + BonsaiWorldStateUpdateAccumulator trxUpdater; + + @BeforeEach + public void setUp() { + collisionDetector = new TransactionCollisionDetector(); + bonsaiUpdater = + new BonsaiWorldStateUpdateAccumulator( + worldState, (__, ___) -> {}, (__, ___) -> {}, EvmConfiguration.DEFAULT); + trxUpdater = + new BonsaiWorldStateUpdateAccumulator( + worldState, (__, ___) -> {}, (__, ___) -> {}, EvmConfiguration.DEFAULT); + } + + private Transaction createTransaction(final Address sender, final Address to) { + return new Transaction.Builder() + .nonce(1) + .gasPrice(Wei.of(1)) + .gasLimit(21000) + .to(to) + .value(Wei.ZERO) + .payload(Bytes.EMPTY) + .chainId(BigInteger.ONE) + .sender(sender) + .build(); + } + + private BonsaiAccount createAccount(final Address address) { + return new BonsaiAccount( + worldState, + address, + Hash.hash(Address.ZERO), + 0, + Wei.ONE, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY, + false); + } + + @Test + void testCollisionWithModifiedBalance() { + final Address address = Address.fromHexString("0x1"); + final BonsaiAccount priorAccountValue = createAccount(address); + final BonsaiAccount nextAccountValue = new BonsaiAccount(priorAccountValue, worldState, true); + nextAccountValue.setBalance(Wei.MAX_WEI); + + // Simulate that the address was already modified in the block + bonsaiUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, nextAccountValue)); + + final Transaction transaction = createTransaction(address, address); + + // Simulate that the address is read in the next transaction + trxUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, priorAccountValue)); + + boolean hasCollision = + collisionDetector.hasCollision( + transaction, + Address.ZERO, + new ParallelizedTransactionContext(trxUpdater, null, false, Wei.ZERO), + bonsaiUpdater); + + assertTrue(hasCollision, "Expected a collision with the modified address"); + } + + @Test + void testCollisionWithModifiedNonce() { + final Address address = Address.fromHexString("0x1"); + final BonsaiAccount priorAccountValue = createAccount(address); + final BonsaiAccount nextAccountValue = new BonsaiAccount(priorAccountValue, worldState, true); + nextAccountValue.setNonce(1); + + // Simulate that the address was already modified in the block + bonsaiUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, nextAccountValue)); + + final Transaction transaction = createTransaction(address, address); + + // Simulate that the address is read in the next transaction + trxUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, priorAccountValue)); + + boolean hasCollision = + collisionDetector.hasCollision( + transaction, + Address.ZERO, + new ParallelizedTransactionContext(trxUpdater, null, false, Wei.ZERO), + bonsaiUpdater); + + assertTrue(hasCollision, "Expected a collision with the modified address"); + } + + @Test + void testCollisionWithModifiedCode() { + final Address address = Address.fromHexString("0x1"); + final BonsaiAccount priorAccountValue = createAccount(address); + final BonsaiAccount nextAccountValue = new BonsaiAccount(priorAccountValue, worldState, true); + nextAccountValue.setCode(Bytes.repeat((byte) 0x01, 10)); + + // Simulate that the address was already modified in the block + bonsaiUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, nextAccountValue)); + + final Transaction transaction = createTransaction(address, address); + + // Simulate that the address is read in the next transaction + trxUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, priorAccountValue)); + + boolean hasCollision = + collisionDetector.hasCollision( + transaction, + Address.ZERO, + new ParallelizedTransactionContext(trxUpdater, null, false, Wei.ZERO), + bonsaiUpdater); + + assertTrue(hasCollision, "Expected a collision with the modified address"); + } + + @Test + void testCollisionWithModifiedStorageRoot() { + final Address address = Address.fromHexString("0x1"); + final BonsaiAccount priorAccountValue = createAccount(address); + final BonsaiAccount nextAccountValue = new BonsaiAccount(priorAccountValue, worldState, true); + nextAccountValue.setStorageRoot(Hash.EMPTY); + + // Simulate that the address was already modified in the block + bonsaiUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, nextAccountValue)); + + final Transaction transaction = createTransaction(address, address); + + // Simulate that the address is read in the next transaction + trxUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, priorAccountValue)); + + boolean hasCollision = + collisionDetector.hasCollision( + transaction, + Address.ZERO, + new ParallelizedTransactionContext(trxUpdater, null, false, Wei.ZERO), + bonsaiUpdater); + + assertTrue(hasCollision, "Expected a collision with the modified address"); + } + + @Test + void testCollisionWithMiningBeneficiaryAddress() { + final Address miningBeneficiary = Address.ZERO; + final Address address = Address.fromHexString("0x1"); + + final Transaction transaction = createTransaction(miningBeneficiary, address); + + boolean hasCollision = + collisionDetector.hasCollision( + transaction, + miningBeneficiary, + new ParallelizedTransactionContext(trxUpdater, null, false, Wei.ZERO), + bonsaiUpdater); + + assertTrue(hasCollision, "Expected collision with the mining beneficiary address as sender"); + } + + @Test + void testCollisionWithAnotherMiningBeneficiaryAddress() { + final Address miningBeneficiary = Address.ZERO; + final Address address = Address.fromHexString("0x1"); + final BonsaiAccount miningBeneficiaryValue = createAccount(address); + + final Transaction transaction = createTransaction(address, address); + + // Simulate that the mining beneficiary is read in the next transaction + trxUpdater + .getAccountsToUpdate() + .put( + miningBeneficiary, + new DiffBasedValue<>(miningBeneficiaryValue, miningBeneficiaryValue)); + + boolean hasCollision = + collisionDetector.hasCollision( + transaction, + miningBeneficiary, + new ParallelizedTransactionContext(trxUpdater, null, true, Wei.ZERO), + bonsaiUpdater); + + assertTrue(hasCollision, "Expected collision with the read mining beneficiary address"); + } + + @Test + void testCollisionWithDeletedAddress() { + final Address address = Address.fromHexString("0x1"); + final BonsaiAccount accountValue = createAccount(address); + + // Simulate that the address was deleted in the block + bonsaiUpdater.getDeletedAccountAddresses().add(address); + + final Transaction transaction = createTransaction(address, address); + + // Simulate that the deleted address is read in the next transaction + trxUpdater.getAccountsToUpdate().put(address, new DiffBasedValue<>(accountValue, accountValue)); + + boolean hasCollision = + collisionDetector.hasCollision( + transaction, + Address.ZERO, + new ParallelizedTransactionContext(trxUpdater, null, false, Wei.ZERO), + bonsaiUpdater); + + assertTrue(hasCollision, "Expected a collision with the deleted address"); + } + + @Test + void testCollisionWithNoModifiedAddress() { + final Address address = Address.fromHexString("0x1"); + final BonsaiAccount priorAccountValue = createAccount(address); + + // Simulate that the address was already read in the block + bonsaiUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, priorAccountValue)); + + final Transaction transaction = createTransaction(address, address); + + // Simulate that the address is read in the next transaction + trxUpdater + .getAccountsToUpdate() + .put(address, new DiffBasedValue<>(priorAccountValue, priorAccountValue)); + + boolean hasCollision = + collisionDetector.hasCollision( + transaction, + Address.ZERO, + new ParallelizedTransactionContext(trxUpdater, null, false, Wei.ZERO), + bonsaiUpdater); + + assertFalse(hasCollision, "Expected no collision with the read address"); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java index 845b859ef7..388a112699 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/AbstractIsolationTests.java @@ -105,7 +105,9 @@ public abstract class AbstractIsolationTests { MainnetProtocolSchedule.fromConfig( GenesisConfigFile.fromResource("/dev.json").getConfigOptions(), MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); protected final GenesisState genesisState = GenesisState.fromConfig(GenesisConfigFile.fromResource("/dev.json"), protocolSchedule); protected final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/trielog/TrieLogPrunerTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/trielog/TrieLogPrunerTest.java index bca954a493..621e73711a 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/trielog/TrieLogPrunerTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/trielog/TrieLogPrunerTest.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldSt import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogAddedEvent; import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogLayer; import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogPruner; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.util.Optional; import java.util.function.Consumer; @@ -72,7 +73,8 @@ public class TrieLogPrunerTest { // When TrieLogPruner trieLogPruner = - new TrieLogPruner(worldState, blockchain, executeAsync, 3, loadingLimit, false); + new TrieLogPruner( + worldState, blockchain, executeAsync, 3, loadingLimit, false, new NoOpMetricsSystem()); trieLogPruner.initialize(); // Then @@ -92,7 +94,13 @@ public class TrieLogPrunerTest { // requireFinalizedBlock = false means this is not a PoS chain TrieLogPruner trieLogPruner = new TrieLogPruner( - worldState, blockchain, executeAsync, blocksToRetain, pruningWindowSize, false); + worldState, + blockchain, + executeAsync, + blocksToRetain, + pruningWindowSize, + false, + new NoOpMetricsSystem()); trieLogPruner.addToPruneQueue(0, key(0)); // older block outside prune window trieLogPruner.addToPruneQueue(1, key(1)); // block inside the prune window @@ -201,7 +209,13 @@ public class TrieLogPrunerTest { when(blockchain.getChainHeadBlockNumber()).thenReturn(chainHeight); TrieLogPruner trieLogPruner = new TrieLogPruner( - worldState, blockchain, executeAsync, blocksToRetain, pruningWindowSize, true); + worldState, + blockchain, + executeAsync, + blocksToRetain, + pruningWindowSize, + true, + new NoOpMetricsSystem()); trieLogPruner.addToPruneQueue(1, key(1)); trieLogPruner.addToPruneQueue(2, key(2)); @@ -240,7 +254,8 @@ public class TrieLogPrunerTest { // Given final TriggerableConsumer triggerableConsumer = new TriggerableConsumer(); TrieLogPruner trieLogPruner = - new TrieLogPruner(worldState, blockchain, triggerableConsumer, 0, 1, false); + new TrieLogPruner( + worldState, blockchain, triggerableConsumer, 0, 1, false, new NoOpMetricsSystem()); assertThat(trieLogPruner.pruneFromQueue()).isEqualTo(0); final TrieLogLayer layer = new TrieLogLayer(); @@ -261,7 +276,8 @@ public class TrieLogPrunerTest { public void onTrieLogAdded_should_not_prune_when_no_blockNumber() { // Given TrieLogPruner trieLogPruner = - new TrieLogPruner(worldState, blockchain, executeAsync, 0, 1, false); + new TrieLogPruner( + worldState, blockchain, executeAsync, 0, 1, false, new NoOpMetricsSystem()); assertThat(trieLogPruner.pruneFromQueue()).isEqualTo(0); final TrieLogLayer layer = new TrieLogLayer(); @@ -289,7 +305,13 @@ public class TrieLogPrunerTest { when(blockchain.getChainHeadBlockNumber()).thenReturn(chainHeight); TrieLogPruner trieLogPruner = new TrieLogPruner( - worldState, blockchain, executeAsync, blocksToRetain, pruningWindowSize, true); + worldState, + blockchain, + executeAsync, + blocksToRetain, + pruningWindowSize, + true, + new NoOpMetricsSystem()); trieLogPruner.addToPruneQueue(1, key(1)); trieLogPruner.addToPruneQueue(2, key(2)); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java index 8cc907d686..755283b080 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java @@ -710,7 +710,11 @@ public class EthPeer implements Comparable { } } // Otherwise, keep older connection - LOG.trace("comparing timestamps " + a.getInitiatedAt() + " with " + b.getInitiatedAt()); + LOG.atTrace() + .setMessage("comparing timestamps {} with {}") + .addArgument(a.getInitiatedAt()) + .addArgument(b.getInitiatedAt()) + .log(); return a.getInitiatedAt() < b.getInitiatedAt() ? -1 : 1; } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthScheduler.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthScheduler.java index dcb6669666..1e2f3eb6ab 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthScheduler.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthScheduler.java @@ -243,7 +243,7 @@ public class EthScheduler { public void stop() { if (stopped.compareAndSet(false, true)) { - LOG.trace("Stopping " + getClass().getSimpleName()); + LOG.atTrace().setMessage("Stopping {}").addArgument(getClass().getSimpleName()).log(); syncWorkerExecutor.shutdownNow(); txWorkerExecutor.shutdownNow(); scheduler.shutdownNow(); @@ -251,7 +251,10 @@ public class EthScheduler { computationExecutor.shutdownNow(); shutdown.countDown(); } else { - LOG.trace("Attempted to stop already stopped " + getClass().getSimpleName()); + LOG.atTrace() + .setMessage("Attempted to stop already stopped {}") + .addArgument(getClass().getSimpleName()) + .log(); } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java index c65a1c6b0c..ce639a7a9a 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java @@ -97,7 +97,7 @@ public class SnapProtocolManager implements ProtocolManager { final EthPeer ethPeer = ethPeers.peer(message.getConnection()); if (ethPeer == null) { LOG.debug( - "Ignoring message received from unknown peer connection: " + message.getConnection()); + "Ignoring message received from unknown peer connection: {}", message.getConnection()); return; } final EthMessage ethMessage = new EthMessage(ethPeer, messageData); @@ -132,9 +132,10 @@ public class SnapProtocolManager implements ProtocolManager { try { ethPeer.send(responseData, getSupportedProtocol()); } catch (final PeerConnection.PeerNotConnected error) { - // Peer disconnected before we could respond - nothing to do - LOG.trace( - "Peer disconnected before we could respond - nothing to do " + error.getMessage()); + LOG.atTrace() + .setMessage("Peer disconnected before we could respond - nothing to do {}") + .addArgument(error.getMessage()) + .log(); } }); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java index 79e8c368e0..bd98bee5ad 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java @@ -50,18 +50,20 @@ public abstract class PendingTransaction private final Transaction transaction; private final long addedAt; private final long sequence; // Allows prioritization based on order transactions are added + private volatile byte score; private int memorySize = NOT_INITIALIZED; private PendingTransaction( - final Transaction transaction, final long addedAt, final long sequence) { + final Transaction transaction, final long addedAt, final long sequence, final byte score) { this.transaction = transaction; this.addedAt = addedAt; this.sequence = sequence; + this.score = score; } private PendingTransaction(final Transaction transaction, final long addedAt) { - this(transaction, addedAt, TRANSACTIONS_ADDED.getAndIncrement()); + this(transaction, addedAt, TRANSACTIONS_ADDED.getAndIncrement(), Byte.MAX_VALUE); } public static PendingTransaction newPendingTransaction( @@ -123,6 +125,20 @@ public abstract class PendingTransaction return memorySize; } + public byte getScore() { + return score; + } + + public void decrementScore() { + // use temp var to avoid non-atomic update of volatile var + final byte newScore = (byte) (score - 1); + + // check to avoid underflow + if (newScore < score) { + score = newScore; + } + } + public abstract PendingTransaction detachedCopy(); private int computeMemorySize() { @@ -131,6 +147,7 @@ public abstract class PendingTransaction case ACCESS_LIST -> computeAccessListMemorySize(); case EIP1559 -> computeEIP1559MemorySize(); case BLOB -> computeBlobMemorySize(); + case SET_CODE -> computeSetCodeMemorySize(); } + PENDING_TRANSACTION_MEMORY_SIZE; } @@ -164,6 +181,10 @@ public abstract class PendingTransaction + computeBlobWithCommitmentsMemorySize(); } + private int computeSetCodeMemorySize() { + return 0; + } + private int computeBlobWithCommitmentsMemorySize() { final int blobCount = transaction.getBlobCount(); @@ -250,6 +271,8 @@ public abstract class PendingTransaction + isReceivedFromLocalSource() + ", hasPriority=" + hasPriority() + + ", score=" + + score + '}'; } @@ -262,6 +285,8 @@ public abstract class PendingTransaction + isReceivedFromLocalSource() + ", hasPriority=" + hasPriority() + + ", score=" + + score + ", " + transaction.toTraceLog() + "}"; @@ -277,13 +302,13 @@ public abstract class PendingTransaction this(transaction, System.currentTimeMillis()); } - private Local(final long sequence, final Transaction transaction) { - super(transaction, System.currentTimeMillis(), sequence); + private Local(final long sequence, final byte score, final Transaction transaction) { + super(transaction, System.currentTimeMillis(), sequence, score); } @Override public PendingTransaction detachedCopy() { - return new Local(getSequence(), getTransaction().detachedCopy()); + return new Local(getSequence(), getScore(), getTransaction().detachedCopy()); } @Override @@ -305,13 +330,13 @@ public abstract class PendingTransaction super(transaction, addedAt); } - public Priority(final long sequence, final Transaction transaction) { - super(sequence, transaction); + public Priority(final long sequence, final byte score, final Transaction transaction) { + super(sequence, score, transaction); } @Override public PendingTransaction detachedCopy() { - return new Priority(getSequence(), getTransaction().detachedCopy()); + return new Priority(getSequence(), getScore(), getTransaction().detachedCopy()); } @Override @@ -331,13 +356,13 @@ public abstract class PendingTransaction this(transaction, System.currentTimeMillis()); } - private Remote(final long sequence, final Transaction transaction) { - super(transaction, System.currentTimeMillis(), sequence); + private Remote(final long sequence, final byte score, final Transaction transaction) { + super(transaction, System.currentTimeMillis(), sequence, score); } @Override public PendingTransaction detachedCopy() { - return new Remote(getSequence(), getTransaction().detachedCopy()); + return new Remote(getSequence(), getScore(), getTransaction().detachedCopy()); } @Override @@ -359,13 +384,13 @@ public abstract class PendingTransaction super(transaction, addedAt); } - public Priority(final long sequence, final Transaction transaction) { - super(sequence, transaction); + public Priority(final long sequence, final byte score, final Transaction transaction) { + super(sequence, score, transaction); } @Override public PendingTransaction detachedCopy() { - return new Priority(getSequence(), getTransaction().detachedCopy()); + return new Priority(getSequence(), getScore(), getTransaction().detachedCopy()); } @Override diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolMetrics.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolMetrics.java index 812e7de9f4..90e9628e5c 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolMetrics.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolMetrics.java @@ -37,12 +37,14 @@ public class TransactionPoolMetrics { public static final String ADDED_COUNTER_NAME = "added_total"; public static final String REMOVED_COUNTER_NAME = "removed_total"; public static final String REJECTED_COUNTER_NAME = "rejected_total"; + public static final String PENALIZED_COUNTER_NAME = "penalized_total"; public static final String EXPIRED_MESSAGES_COUNTER_NAME = "messages_expired_total"; private static final int SKIPPED_MESSAGES_LOGGING_THRESHOLD = 1000; private final MetricsSystem metricsSystem; private final LabelledMetric addedCounter; private final LabelledMetric removedCounter; private final LabelledMetric rejectedCounter; + private final LabelledMetric penalizedCounter; private final LabelledGauge spaceUsed; private final LabelledGauge transactionCount; private final LabelledGauge transactionCountByType; @@ -88,6 +90,15 @@ public class TransactionPoolMetrics { "reason", "layer"); + penalizedCounter = + metricsSystem.createLabelledCounter( + BesuMetricCategory.TRANSACTION_POOL, + PENALIZED_COUNTER_NAME, + "Count of penalized transactions in the transaction pool", + "source", + "priority", + "layer"); + spaceUsed = metricsSystem.createLabelledGauge( BesuMetricCategory.TRANSACTION_POOL, @@ -246,6 +257,15 @@ public class TransactionPoolMetrics { .inc(); } + public void incrementPenalized(final PendingTransaction pendingTransaction, final String layer) { + penalizedCounter + .labels( + location(pendingTransaction.isReceivedFromLocalSource()), + priority(pendingTransaction.hasPriority()), + layer) + .inc(); + } + public void incrementExpiredMessages(final String message) { expiredMessagesCounter.labels(message).inc(); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractPrioritizedTransactions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractPrioritizedTransactions.java index 7a65b3febc..470e7f5b7b 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractPrioritizedTransactions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractPrioritizedTransactions.java @@ -24,13 +24,17 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NavigableMap; +import java.util.TreeMap; import java.util.TreeSet; import java.util.function.BiFunction; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Holds the current set of executable pending transactions, that are candidate for inclusion on @@ -137,6 +141,13 @@ public abstract class AbstractPrioritizedTransactions extends AbstractSequential orderByFee.remove(removedTx); } + @Override + protected void internalPenalize(final PendingTransaction penalizedTx) { + orderByFee.remove(penalizedTx); + penalizedTx.decrementScore(); + orderByFee.add(penalizedTx); + } + @Override public List promote( final Predicate promotionFilter, @@ -188,6 +199,60 @@ public abstract class AbstractPrioritizedTransactions extends AbstractSequential .toList(); } + /** + * Returns pending txs by sender and ordered by score desc. In case a sender has pending txs with + * different scores, then in nonce sequence, every time there is a score decrease, his pending txs + * will be put in a new entry with that score. For example if a sender has 3 pending txs (where + * the first number is the nonce and the score is between parenthesis): 0(127), 1(126), 2(127), + * then for he there will be 2 entries: + * + *

    + *
  • 0(127) + *
  • 1(126), 2(127) + *
+ * + * @return pending txs by sender and ordered by score desc + */ + public NavigableMap> getByScore() { + final var sendersToAdd = new HashSet<>(txsBySender.keySet()); + return orderByFee.descendingSet().stream() + .map(PendingTransaction::getSender) + .filter(sendersToAdd::remove) + .flatMap(sender -> splitByScore(sender, txsBySender.get(sender)).entrySet().stream()) + .collect( + Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (a, b) -> { + a.addAll(b); + return a; + }, + TreeMap::new)) + .descendingMap(); + } + + private Map> splitByScore( + final Address sender, final NavigableMap txsBySender) { + final var splitByScore = new HashMap>(); + byte currScore = txsBySender.firstEntry().getValue().getScore(); + var currSplit = new ArrayList(); + for (final var entry : txsBySender.entrySet()) { + if (entry.getValue().getScore() < currScore) { + // score decreased, we need to save current split and start a new one + splitByScore + .computeIfAbsent(currScore, k -> new ArrayList<>()) + .add(new SenderPendingTransactions(sender, currSplit)); + currSplit = new ArrayList<>(); + currScore = entry.getValue().getScore(); + } + currSplit.add(entry.getValue()); + } + splitByScore + .computeIfAbsent(currScore, k -> new ArrayList<>()) + .add(new SenderPendingTransactions(sender, currSplit)); + return splitByScore; + } + @Override protected long cacheFreeSpace() { return Integer.MAX_VALUE; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractTransactionsLayer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractTransactionsLayer.java index 5997fe6c18..b4f6e927c0 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractTransactionsLayer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/AbstractTransactionsLayer.java @@ -463,6 +463,18 @@ public abstract class AbstractTransactionsLayer implements TransactionsLayer { } } + @Override + public void penalize(final PendingTransaction penalizedTransaction) { + if (pendingTransactions.containsKey(penalizedTransaction.getHash())) { + internalPenalize(penalizedTransaction); + metrics.incrementPenalized(penalizedTransaction, name()); + } else { + nextLayer.penalize(penalizedTransaction); + } + } + + protected abstract void internalPenalize(final PendingTransaction pendingTransaction); + /** * How many txs of a specified type can be promoted? This make sense when a max number of txs of a * type can be included in a single block (ex. blob txs), to avoid filling the layer with more txs diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseFeePrioritizedTransactions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseFeePrioritizedTransactions.java index 8140cd5d7a..b3dec34b77 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseFeePrioritizedTransactions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseFeePrioritizedTransactions.java @@ -19,7 +19,6 @@ import static org.hyperledger.besu.ethereum.eth.transactions.layered.Transaction import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.MiningParameters; -import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; @@ -66,7 +65,8 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti @Override protected int compareByFee(final PendingTransaction pt1, final PendingTransaction pt2) { - return Comparator.comparing(PendingTransaction::hasPriority) + return Comparator.comparing(PendingTransaction::getScore) + .thenComparing(PendingTransaction::hasPriority) .thenComparing( (PendingTransaction pendingTransaction) -> pendingTransaction.getTransaction().getEffectivePriorityFeePerGas(nextBlockBaseFee)) @@ -195,8 +195,8 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti return "Basefee Prioritized: Empty"; } - final Transaction highest = orderByFee.last().getTransaction(); - final Transaction lowest = orderByFee.first().getTransaction(); + final PendingTransaction highest = orderByFee.last(); + final PendingTransaction lowest = orderByFee.first(); return "Basefee Prioritized: " + "count: " @@ -205,16 +205,26 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti + spaceUsed + ", unique senders: " + txsBySender.size() - + ", highest priority tx: [max fee: " - + highest.getMaxGasPrice().toHumanReadableString() + + ", highest priority tx: [score: " + + highest.getScore() + + ", max fee: " + + highest.getTransaction().getMaxGasPrice().toHumanReadableString() + ", curr prio fee: " - + highest.getEffectivePriorityFeePerGas(nextBlockBaseFee).toHumanReadableString() + + highest + .getTransaction() + .getEffectivePriorityFeePerGas(nextBlockBaseFee) + .toHumanReadableString() + ", hash: " + highest.getHash() - + "], lowest priority tx: [max fee: " - + lowest.getMaxGasPrice().toHumanReadableString() + + "], lowest priority tx: [score: " + + lowest.getScore() + + ", max fee: " + + lowest.getTransaction().getMaxGasPrice().toHumanReadableString() + ", curr prio fee: " - + lowest.getEffectivePriorityFeePerGas(nextBlockBaseFee).toHumanReadableString() + + lowest + .getTransaction() + .getEffectivePriorityFeePerGas(nextBlockBaseFee) + .toHumanReadableString() + ", hash: " + lowest.getHash() + "], next block base fee: " diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/EndLayer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/EndLayer.java index 0f5d9a6d15..f383f178c2 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/EndLayer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/EndLayer.java @@ -85,6 +85,9 @@ public class EndLayer implements TransactionsLayer { @Override public void remove(final PendingTransaction pendingTransaction, final RemovalReason reason) {} + @Override + public void penalize(final PendingTransaction penalizedTx) {} + @Override public void blockAdded( final FeeMarket feeMarket, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/GasPricePrioritizedTransactions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/GasPricePrioritizedTransactions.java index 97bd3a88ee..504a453fa8 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/GasPricePrioritizedTransactions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/GasPricePrioritizedTransactions.java @@ -56,7 +56,8 @@ public class GasPricePrioritizedTransactions extends AbstractPrioritizedTransact @Override protected int compareByFee(final PendingTransaction pt1, final PendingTransaction pt2) { - return comparing(PendingTransaction::hasPriority) + return comparing(PendingTransaction::getScore) + .thenComparing(PendingTransaction::hasPriority) .thenComparing(PendingTransaction::getGasPrice) .thenComparing(PendingTransaction::getSequence) .compare(pt1, pt2); @@ -78,21 +79,33 @@ public class GasPricePrioritizedTransactions extends AbstractPrioritizedTransact } @Override - public String internalLogStats() { + protected String internalLogStats() { if (orderByFee.isEmpty()) { return "GasPrice Prioritized: Empty"; } + final PendingTransaction highest = orderByFee.last(); + final PendingTransaction lowest = orderByFee.first(); + return "GasPrice Prioritized: " + "count: " + pendingTransactions.size() - + " space used: " + + ", space used: " + spaceUsed - + " unique senders: " + + ", unique senders: " + txsBySender.size() - + ", highest fee tx: " - + orderByFee.last().getTransaction().getGasPrice().get().toHumanReadableString() - + ", lowest fee tx: " - + orderByFee.first().getTransaction().getGasPrice().get().toHumanReadableString(); + + ", highest priority tx: [score: " + + highest.getScore() + + ", gas price: " + + highest.getTransaction().getGasPrice().get().toHumanReadableString() + + ", hash: " + + highest.getHash() + + "], lowest priority tx: [score: " + + lowest.getScore() + + ", gas price: " + + lowest.getTransaction().getGasPrice().get().toHumanReadableString() + + ", hash: " + + lowest.getHash() + + "]"; } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java index 9444885ebe..5297f08021 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java @@ -42,10 +42,12 @@ import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalLong; +import java.util.Set; import java.util.stream.Collector; import java.util.stream.Collectors; @@ -314,55 +316,80 @@ public class LayeredPendingTransactions implements PendingTransactions { @Override public void selectTransactions(final PendingTransactions.TransactionSelector selector) { final List invalidTransactions = new ArrayList<>(); + final List penalizedTransactions = new ArrayList<>(); + final Set
skipSenders = new HashSet<>(); - final List candidateTxsBySender; + final Map> candidateTxsByScore; synchronized (this) { // since selecting transactions for block creation is a potential long operation // we want to avoid to keep the lock for all the process, but we just lock to get // the candidate transactions - candidateTxsBySender = prioritizedTransactions.getBySender(); + candidateTxsByScore = prioritizedTransactions.getByScore(); } selection: - for (final var senderTxs : candidateTxsBySender) { - LOG.trace("highPrioSenderTxs {}", senderTxs); - - for (final var candidatePendingTx : senderTxs.pendingTransactions()) { - final var selectionResult = selector.evaluateTransaction(candidatePendingTx); - - LOG.atTrace() - .setMessage("Selection result {} for transaction {}") - .addArgument(selectionResult) - .addArgument(candidatePendingTx::toTraceLog) - .log(); - - if (selectionResult.discard()) { - invalidTransactions.add(candidatePendingTx); - logDiscardedTransaction(candidatePendingTx, selectionResult); - } - - if (selectionResult.stop()) { - LOG.trace("Stopping selection"); - break selection; - } - - if (!selectionResult.selected()) { - // avoid processing other txs from this sender if this one is skipped - // since the following will not be selected due to the nonce gap - LOG.trace("Skipping remaining txs for sender {}", candidatePendingTx.getSender()); - break; + for (final var entry : candidateTxsByScore.entrySet()) { + LOG.trace("Evaluating txs with score {}", entry.getKey()); + + for (final var senderTxs : entry.getValue()) { + LOG.trace("Evaluating sender txs {}", senderTxs); + + if (!skipSenders.contains(senderTxs.sender())) { + + for (final var candidatePendingTx : senderTxs.pendingTransactions()) { + final var selectionResult = selector.evaluateTransaction(candidatePendingTx); + + LOG.atTrace() + .setMessage("Selection result {} for transaction {}") + .addArgument(selectionResult) + .addArgument(candidatePendingTx::toTraceLog) + .log(); + + if (selectionResult.discard()) { + invalidTransactions.add(candidatePendingTx); + logDiscardedTransaction(candidatePendingTx, selectionResult); + } + + if (selectionResult.penalize()) { + penalizedTransactions.add(candidatePendingTx); + LOG.atTrace() + .setMessage("Transaction {} penalized") + .addArgument(candidatePendingTx::toTraceLog) + .log(); + } + + if (selectionResult.stop()) { + LOG.trace("Stopping selection"); + break selection; + } + + if (!selectionResult.selected()) { + // avoid processing other txs from this sender if this one is skipped + // since the following will not be selected due to the nonce gap + LOG.trace("Skipping remaining txs for sender {}", candidatePendingTx.getSender()); + skipSenders.add(candidatePendingTx.getSender()); + break; + } + } } } } ethScheduler.scheduleTxWorkerTask( - () -> - invalidTransactions.forEach( - invalidTx -> { - synchronized (this) { - prioritizedTransactions.remove(invalidTx, INVALIDATED); - } - })); + () -> { + invalidTransactions.forEach( + invalidTx -> { + synchronized (this) { + prioritizedTransactions.remove(invalidTx, INVALIDATED); + } + }); + penalizedTransactions.forEach( + penalizedTx -> { + synchronized (this) { + prioritizedTransactions.internalPenalize(penalizedTx); + } + }); + }); } @Override diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/ReadyTransactions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/ReadyTransactions.java index 1f9fc0ab8d..0f52e1c5c3 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/ReadyTransactions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/ReadyTransactions.java @@ -18,7 +18,6 @@ import static org.hyperledger.besu.ethereum.eth.transactions.layered.Transaction import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction; @@ -43,7 +42,8 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer { private final NavigableSet orderByMaxFee = new TreeSet<>( - Comparator.comparing(PendingTransaction::hasPriority) + Comparator.comparing(PendingTransaction::getScore) + .thenComparing(PendingTransaction::hasPriority) .thenComparing((PendingTransaction pt) -> pt.getTransaction().getMaxGasPrice()) .thenComparing(PendingTransaction::getSequence)); @@ -116,6 +116,20 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer { } } + @Override + protected void internalPenalize(final PendingTransaction penalizedTx) { + final var senderTxs = txsBySender.get(penalizedTx.getSender()); + if (senderTxs.firstKey() == penalizedTx.getNonce()) { + // since we only sort the first tx of sender, we only need to re-sort in this case + orderByMaxFee.remove(penalizedTx); + penalizedTx.decrementScore(); + orderByMaxFee.add(penalizedTx); + } else { + // otherwise we just decrement the score + penalizedTx.decrementScore(); + } + } + @Override protected void internalReplaced(final PendingTransaction replacedTx) { orderByMaxFee.remove(replacedTx); @@ -213,8 +227,8 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer { return "Ready: Empty"; } - final Transaction top = orderByMaxFee.last().getTransaction(); - final Transaction last = orderByMaxFee.first().getTransaction(); + final PendingTransaction top = orderByMaxFee.last(); + final PendingTransaction last = orderByMaxFee.first(); return "Ready: " + "count=" @@ -223,12 +237,16 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer { + spaceUsed + ", unique senders: " + txsBySender.size() - + ", top by max fee[max fee:" - + top.getMaxGasPrice().toHumanReadableString() + + ", top by score and max gas price[score: " + + top.getScore() + + ", max gas price:" + + top.getTransaction().getMaxGasPrice().toHumanReadableString() + ", hash: " + top.getHash() - + "], last by max fee [max fee: " - + last.getMaxGasPrice().toHumanReadableString() + + "], last by score and max gas price [score: " + + last.getScore() + + ", max fee: " + + last.getTransaction().getMaxGasPrice().toHumanReadableString() + ", hash: " + last.getHash() + "]"; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/SparseTransactions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/SparseTransactions.java index 52f598ba71..aef318e6df 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/SparseTransactions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/SparseTransactions.java @@ -303,6 +303,11 @@ public class SparseTransactions extends AbstractTransactionsLayer { } } + @Override + protected void internalPenalize(final PendingTransaction penalizedTx) { + // intentionally no-op + } + private void deleteGap(final Address sender) { orderByGap.get(gapBySender.remove(sender)).remove(sender); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/TransactionsLayer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/TransactionsLayer.java index d3c22aeef1..531add0af7 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/TransactionsLayer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/TransactionsLayer.java @@ -45,6 +45,19 @@ public interface TransactionsLayer { void remove(PendingTransaction pendingTransaction, RemovalReason reason); + /** + * Penalize a pending transaction. Penalization could be applied to notify the txpool that this + * pending tx has some temporary issues that prevent it from being included in a block, and so it + * should be de-prioritized in some ways, so it will be re-evaluated only after non penalized + * pending txs. For example: if during the evaluation for block inclusion, the pending tx is + * excluded because the sender has not enough balance to send it, this could be a transient issue + * since later the sender could receive some funds, but in any case we penalize the pending tx, so + * it is pushed down in the order of prioritized pending txs. + * + * @param penalizedTransaction the tx to penalize + */ + void penalize(PendingTransaction penalizedTransaction); + void blockAdded( FeeMarket feeMarket, BlockHeader blockHeader, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/package-info.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/package-info.java index fbc9da9fe5..ff274ce4ea 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/package-info.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/package-info.java @@ -22,7 +22,8 @@ * transactions that could be selected for a future block proposal, and at the same time, without * penalizing legitimate unordered transactions, that are only temporary non-executable. * - *

It is disabled by default, to enable use the option {@code Xlayered-tx-pool=true} + *

It is enabled by default on public networks, to switch to another implementation use the + * option {@code tx-pool} * *

The main idea is to organize the txpool in an arbitrary number of layers, where each layer has * specific rules and constraints that determine if a transaction belong or not to that layer and @@ -38,6 +39,14 @@ * transactions are removed since confirmed in a block, transactions from the next layer are * promoted until there is space. * + *

Some layers could make use of the score of a pending transactions, to push back in the rank + * those pending transactions that have been penalized. + * + *

Layers are not thread safe, since they are not meant to be accessed directly, and all the + * synchronization is managed at the level of {@link + * org.hyperledger.besu.ethereum.eth.transactions.layered.LayeredPendingTransactions + * LayeredPendingTransactions} class. + * *

The current implementation is based on 3 layers, plus the last one that just drop every * transaction when the previous layers are full. The 3 layers are, in order: * @@ -48,20 +57,20 @@ * * *

Prioritized: This is where candidate transactions are selected for creating a new block. - * Transactions ordered by the effective priority fee, and it is limited by size, 2000 by default, - * to reduce the overhead of the sorting and because that number is enough to fill any block, at the - * current gas limit. Does not allow nonce gaps, and the first transaction for each sender must be - * the next one for that sender. Eviction is done removing the transaction with the higher nonce for - * the sender of the less valuable transaction, to avoid creating nonce gaps, evicted transactions - * go into the next layer Ready. + * Transactions ordered by score and then effective priority fee, and it is limited by size, 2000 by + * default, to reduce the overhead of the sorting and because that number is enough to fill any + * block, at the current gas limit. Does not allow nonce gaps, and the first transaction for each + * sender must be the next one for that sender. Eviction is done removing the transaction with the + * higher nonce for the sender of the less score and less effective priority fee transaction, to + * avoid creating nonce gaps, evicted transactions go into the next layer Ready. * *

Ready: Similar to the Prioritized, it does not allow nonce gaps, and the first transaction for * each sender must be the next one for that sender, but it is limited by space instead of count, * thus allowing many more transactions, think about this layer like a buffer for the Prioritized. * Since it is meant to keep ten to hundreds of thousand of transactions, it does not have a full * ordering, like the previous, but only the first transaction for each sender is ordered using a - * stable value that is the max fee per gas. Eviction is the same as the Prioritized, and evicted - * transaction go into the next layer Sparse. + * stable value that is score and then max fee per gas. Eviction is the same as the Prioritized, and + * evicted transaction go into the next layer Sparse. * *

Sparse: This is the first layer where nonce gaps are allowed and where the first transaction * for a sender could not be the next expected one for that sender. The main purpose of this layer diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java index 93a80be2fd..3a3331b568 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java @@ -64,6 +64,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.BlobCache; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory; +import org.hyperledger.besu.ethereum.forkid.ForkId; import org.hyperledger.besu.ethereum.forkid.ForkIdManager; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; @@ -93,6 +94,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import java.util.zip.CRC32; import com.google.common.collect.Lists; import org.apache.tuweni.bytes.Bytes; @@ -1136,7 +1138,7 @@ public final class EthProtocolManagerTest { } @Test - public void forkIdForChainHeadMayBeNull() { + public void forkIdForChainHeadLegacyNoForksNotEmpty() { final EthScheduler ethScheduler = mock(EthScheduler.class); try (final EthProtocolManager ethManager = EthProtocolManagerTestUtil.create( @@ -1149,7 +1151,13 @@ public final class EthProtocolManagerTest { new ForkIdManager( blockchain, Collections.emptyList(), Collections.emptyList(), true))) { - assertThat(ethManager.getForkIdAsBytesList()).isEmpty(); + assertThat(ethManager.getForkIdAsBytesList()).isNotEmpty(); + final CRC32 genesisHashCRC = new CRC32(); + genesisHashCRC.update(blockchain.getGenesisBlock().getHash().toArray()); + assertThat(ethManager.getForkIdAsBytesList()) + .isEqualTo( + new ForkId(Bytes.ofUnsignedInt(genesisHashCRC.getValue()), 0L) + .getForkIdAsBytesList()); } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/BlockBodiesMessageTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/BlockBodiesMessageTest.java index ac3b8f2fc6..b11afeb4c5 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/BlockBodiesMessageTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/BlockBodiesMessageTest.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPException; import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.io.IOException; import java.nio.ByteBuffer; @@ -60,7 +61,9 @@ public final class BlockBodiesMessageTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } @Test diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/BlockHeadersMessageTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/BlockHeadersMessageTest.java index 2b9105ed46..72f089a53e 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/BlockHeadersMessageTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/BlockHeadersMessageTest.java @@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.io.IOException; import java.nio.ByteBuffer; @@ -67,7 +68,9 @@ public final class BlockHeadersMessageTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager())); + new BadBlockManager(), + false, + new NoOpMetricsSystem())); for (int i = 0; i < 50; ++i) { Assertions.assertThat(readHeaders.get(i)).isEqualTo(headers.get(i)); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/ChainHeadTrackerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/ChainHeadTrackerTest.java index 4bad73ebb6..5f7baaff41 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/ChainHeadTrackerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/ChainHeadTrackerTest.java @@ -59,7 +59,9 @@ public class ChainHeadTrackerTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); private final TrailingPeerLimiter trailingPeerLimiter = mock(TrailingPeerLimiter.class); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java index 9afba66ad3..cfc9f9dc12 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java @@ -48,6 +48,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.referencetests.ForestReferenceTestWorldState; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; @@ -93,7 +94,11 @@ public class BackwardSyncContextTest { @Spy private ProtocolSchedule protocolSchedule = MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions(), MiningParameters.MINING_DISABLED, new BadBlockManager()); + new StubGenesisConfigOptions(), + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()); @Spy private ProtocolSpec protocolSpec = diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncStepTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncStepTest.java index ecb72881ed..6dc69ea274 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncStepTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncStepTest.java @@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.eth.manager.exceptions.MaxRetriesReachedExc import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.testutil.DeterministicEthScheduler; @@ -70,7 +71,11 @@ public class BackwardSyncStepTest { private final ProtocolSchedule protocolSchedule = MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions(), MiningParameters.MINING_DISABLED, new BadBlockManager()); + new StubGenesisConfigOptions(), + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()); private final DeterministicEthScheduler ethScheduler = new DeterministicEthScheduler(); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java index 5fb47a557a..a5d2cf6104 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java @@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.referencetests.ForestReferenceTestWorldState; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import java.nio.charset.StandardCharsets; @@ -73,7 +74,11 @@ public class ForwardSyncStepTest { private final ProtocolSchedule protocolSchedule = MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions(), MiningParameters.MINING_DISABLED, new BadBlockManager()); + new StubGenesisConfigOptions(), + MiningParameters.MINING_DISABLED, + new BadBlockManager(), + false, + new NoOpMetricsSystem()); private MutableBlockchain localBlockchain; GenericKeyValueStorageFacade headersStorage; GenericKeyValueStorageFacade blocksStorage; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java index 23eb9d02bf..cd780c2dc9 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java @@ -201,7 +201,9 @@ public abstract class AbstractTransactionPoolTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); final ExecutionContextTestFixture executionContextTestFixture = ExecutionContextTestFixture.builder(genesisConfigFile) diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java index 8e9bda89c0..c679183b0f 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java @@ -123,7 +123,9 @@ public class TestNode implements Closeable { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()); + new BadBlockManager(), + false, + new NoOpMetricsSystem()); final GenesisState genesisState = GenesisState.fromConfig(genesisConfigFile, protocolSchedule); final BlockHeaderFunctions blockHeaderFunctions = diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java index ef8d070178..5742637c3e 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java @@ -379,7 +379,9 @@ public class TransactionPoolFactoryTest { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); protocolContext = mock(ProtocolContext.class); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseTransactionPoolTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseTransactionPoolTest.java index e717adbed1..bbd4e7322f 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseTransactionPoolTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseTransactionPoolTest.java @@ -128,7 +128,7 @@ public class BaseTransactionPoolTest { final TransactionType txType = TransactionType.values()[randomizeTxType.nextInt(4)]; return switch (txType) { - case FRONTIER, ACCESS_LIST, EIP1559 -> + case FRONTIER, ACCESS_LIST, EIP1559, SET_CODE -> createTransaction(txType, nonce, maxGasPrice, payloadSize, keys); case BLOB -> createTransaction( diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayersTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayersTest.java index a56222345c..84ac759a79 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayersTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayersTest.java @@ -159,6 +159,12 @@ public class LayersTest extends BaseTransactionPoolTest { assertScenario(scenario, BLOB_TX_POOL_CONFIG); } + @ParameterizedTest + @MethodSource("providerPenalized") + void penalized(final Scenario scenario) { + assertScenario(scenario); + } + private void assertScenario(final Scenario scenario) { assertScenario(scenario, DEFAULT_TX_POOL_CONFIG); } @@ -1256,6 +1262,79 @@ public class LayersTest extends BaseTransactionPoolTest { .expectedSparseForSenders())); } + static Stream providerPenalized() { + return Stream.of( + Arguments.of( + new Scenario("single sender, single tx") + .addForSender(S1, 0) + .expectedPrioritizedForSender(S1, 0) + .penalizeForSender(S1, 0) + .expectedPrioritizedForSender(S1, 0)), + Arguments.of( + new Scenario("single sender penalize last") + .addForSender(S1, 0, 1) + .expectedPrioritizedForSender(S1, 0, 1) + .penalizeForSender(S1, 1) + .expectedPrioritizedForSender(S1, 0, 1)), + Arguments.of( + new Scenario("single sender penalize first") + .addForSender(S1, 0, 1) + .expectedPrioritizedForSender(S1, 0, 1) + .penalizeForSender(S1, 0, 1) + // even if 0 has less score it is always the first for the sender + // since otherwise there is a nonce gap + .expectedPrioritizedForSender(S1, 0, 1)), + Arguments.of( + new Scenario("multiple senders, penalize top") + .addForSenders(S1, 0, S2, 0) + // remember S2 pays more fees + .expectedPrioritizedForSenders(S2, 0, S1, 0) + .penalizeForSender(S2, 0) + .expectedPrioritizedForSenders(S1, 0, S2, 0)), + Arguments.of( + new Scenario("multiple senders, penalize bottom") + .addForSenders(S1, 0, S2, 0) + .expectedPrioritizedForSenders(S2, 0, S1, 0) + .penalizeForSender(S1, 0) + .expectedPrioritizedForSenders(S2, 0, S1, 0)), + Arguments.of( + new Scenario("multiple senders, penalize middle") + .addForSenders(S1, 0, S2, 0, S3, 0) + .expectedPrioritizedForSenders(S3, 0, S2, 0, S1, 0) + .penalizeForSender(S2, 0) + .expectedPrioritizedForSenders(S3, 0, S1, 0, S2, 0)), + Arguments.of( + new Scenario("single sender, promote from ready") + .addForSender(S1, 0, 1, 2, 3, 4, 5) + .expectedPrioritizedForSender(S1, 0, 1, 2) + .expectedReadyForSender(S1, 3, 4, 5) + .penalizeForSender(S1, 3) + .confirmedForSenders(S1, 0) + // even if penalized 3 is promoted to avoid nonce gap + .expectedPrioritizedForSender(S1, 1, 2, 3) + .expectedReadyForSender(S1, 4, 5)), + Arguments.of( + new Scenario("multiple senders, overflow to ready") + .addForSenders(S1, 0, S2, 0, S3, 0) + .expectedPrioritizedForSenders(S3, 0, S2, 0, S1, 0) + .expectedReadyForSenders() + .penalizeForSender(S3, 0) + .addForSender(S1, 1) + .expectedPrioritizedForSenders(S2, 0, S1, 0, S1, 1) + // S3(0) is demoted to ready even if it is paying more fees, + // since has a lower score + .expectedReadyForSender(S3, 0)), + Arguments.of( + new Scenario("multiple senders, overflow to sparse") + .addForSenders(S1, 0, S2, 0, S3, 0, S1, 1, S2, 1, S3, 1) + .expectedPrioritizedForSenders(S3, 0, S3, 1, S2, 0) + .expectedReadyForSenders(S2, 1, S1, 0, S1, 1) + .penalizeForSender(S2, 1) + .addForSender(S2, 2) + .expectedReadyForSenders(S1, 0, S1, 1, S2, 1) + .expectedSparseForSender(S2, 2))); + } + private static BlockHeader mockBlockHeader() { final BlockHeader blockHeader = mock(BlockHeader.class); when(blockHeader.getBaseFee()).thenReturn(Optional.of(BASE_FEE)); @@ -1377,6 +1456,7 @@ public class LayersTest extends BaseTransactionPoolTest { case ACCESS_LIST -> createAccessListPendingTransaction(sender, n); case EIP1559 -> createEIP1559PendingTransaction(sender, n); case BLOB -> createBlobPendingTransaction(sender, n); + case SET_CODE -> throw new UnsupportedOperationException(); }); } @@ -1510,10 +1590,12 @@ public class LayersTest extends BaseTransactionPoolTest { private void assertExpectedPrioritized( final AbstractPrioritizedTransactions prioLayer, final List expected) { - assertThat(prioLayer.getBySender()) - .describedAs("Prioritized") - .flatExtracting(SenderPendingTransactions::pendingTransactions) - .containsExactlyElementsOf(expected); + final var flatOrder = + prioLayer.getByScore().values().stream() + .flatMap(List::stream) + .flatMap(spt -> spt.pendingTransactions().stream()) + .toList(); + assertThat(flatOrder).describedAs("Prioritized").containsExactlyElementsOf(expected); } private void assertExpectedReady( @@ -1581,6 +1663,23 @@ public class LayersTest extends BaseTransactionPoolTest { return this; } + public Scenario penalizeForSender(final Sender sender, final long... nonce) { + Arrays.stream(nonce) + .forEach( + n -> { + actions.add( + (pending, prio, ready, sparse, dropped) -> { + final var senderTxs = prio.getAllFor(sender.address); + Arrays.stream(nonce) + .mapToObj( + n2 -> senderTxs.stream().filter(pt -> pt.getNonce() == n2).findAny()) + .map(Optional::get) + .forEach(prio::penalize); + }); + }); + return this; + } + public Scenario expectedSelectedTransactions(final Object... args) { List expectedSelected = new ArrayList<>(); for (int i = 0; i < args.length; i = i + 2) { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java index db70ccf41f..4f10650273 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/MainnetGenesisFileModule.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.evm.EvmSpecVersion; import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; import java.util.Locale; @@ -80,7 +81,12 @@ class MainnetGenesisFileModule extends GenesisFileModule { } return MainnetProtocolSchedule.fromConfig( - configOptions, evmConfiguration, MiningParameters.newDefault(), new BadBlockManager()); + configOptions, + evmConfiguration, + MiningParameters.newDefault(), + new BadBlockManager(), + false, + new NoOpMetricsSystem()); } public static Map> createSchedules() { @@ -145,7 +151,9 @@ class MainnetGenesisFileModule extends GenesisFileModule { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); } } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java index 064a093e84..40f36f18f1 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java @@ -232,7 +232,7 @@ public class StateTestSubCommand implements Runnable { } final BlockHeader blockHeader = spec.getBlockHeader(); - final Transaction transaction = spec.getTransaction(); + final Transaction transaction = spec.getTransaction(0); final ObjectNode summaryLine = objectMapper.createObjectNode(); if (transaction == null) { if (parentCommand.showJsonAlloc || parentCommand.showJsonResults) { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java index ee6c5d5f70..6f222166b3 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.ConsolidationRequest; import org.hyperledger.besu.ethereum.core.DepositRequest; import org.hyperledger.besu.ethereum.core.Request; +import org.hyperledger.besu.ethereum.core.SetCodeAuthorization; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.WithdrawalRequest; @@ -212,6 +213,69 @@ public class T8nExecutor { builder.accessList(entries); } + if (txNode.has("authorizationList")) { + JsonNode authorizationList = txNode.get("authorizationList"); + + if (!authorizationList.isArray()) { + out.printf( + "TX json node unparseable: expected authorizationList to be an array - %s%n", + txNode); + continue; + } + + List authorizations = + new ArrayList<>(authorizationList.size()); + for (JsonNode entryAsJson : authorizationList) { + final BigInteger authorizationChainId = + Bytes.fromHexStringLenient(entryAsJson.get("chainId").textValue()) + .toUnsignedBigInteger(); + final Address authorizationAddress = + Address.fromHexString(entryAsJson.get("address").textValue()); + + JsonNode nonces = entryAsJson.get("nonce"); + + if (nonces == null) { + out.printf( + "TX json node unparseable: expected nonce field to be provided - %s%n", + txNode); + continue; + } + + List authorizationNonces; + if (nonces.isArray()) { + authorizationNonces = new ArrayList<>(nonces.size()); + for (JsonNode nonceAsJson : nonces) { + authorizationNonces.add( + Bytes.fromHexStringLenient(nonceAsJson.textValue()).toLong()); + } + } else { + authorizationNonces = + List.of(Bytes.fromHexStringLenient(nonces.textValue()).toLong()); + } + + final byte authorizationV = + Bytes.fromHexStringLenient(entryAsJson.get("v").textValue()) + .toUnsignedBigInteger() + .byteValueExact(); + final BigInteger authorizationR = + Bytes.fromHexStringLenient(entryAsJson.get("r").textValue()) + .toUnsignedBigInteger(); + final BigInteger authorizationS = + Bytes.fromHexStringLenient(entryAsJson.get("s").textValue()) + .toUnsignedBigInteger(); + + authorizations.add( + SetCodeAuthorization.createSetCodeAuthorizationEntry( + authorizationChainId, + authorizationAddress, + authorizationNonces, + authorizationV, + authorizationR, + authorizationS)); + } + builder.setCodeTransactionPayloads(authorizations); + } + if (txNode.has("blobVersionedHashes")) { JsonNode blobVersionedHashes = txNode.get("blobVersionedHashes"); if (!blobVersionedHashes.isArray()) { @@ -255,7 +319,10 @@ public class T8nExecutor { Bytes.fromHexStringLenient(txNode.get("s").textValue()) .toUnsignedBigInteger(), v.byteValueExact())); - transactions.add(builder.build()); + + final Transaction tx = builder.build(); + + transactions.add(tx); } } } else { diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java index 86bb079a29..3f59abf2fd 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/config/DiscoveryConfiguration.java @@ -33,6 +33,7 @@ public class DiscoveryConfiguration { private String dnsDiscoveryURL; private boolean discoveryV5Enabled = false; private boolean filterOnEnrForkId = NetworkingConfiguration.DEFAULT_FILTER_ON_ENR_FORK_ID; + private boolean includeBootnodesOnPeerRefresh = true; public static DiscoveryConfiguration create() { return new DiscoveryConfiguration(); @@ -88,6 +89,16 @@ public class DiscoveryConfiguration { return this; } + public boolean getIncludeBootnodesOnPeerRefresh() { + return includeBootnodesOnPeerRefresh; + } + + public DiscoveryConfiguration setIncludeBootnodesOnPeerRefresh( + final boolean includeBootnodesOnPeerRefresh) { + this.includeBootnodesOnPeerRefresh = includeBootnodesOnPeerRefresh; + return this; + } + public String getAdvertisedHost() { return advertisedHost; } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java index d1f17b5304..7efc50750f 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java @@ -274,6 +274,7 @@ public abstract class PeerDiscoveryAgent { .filterOnEnrForkId((config.isFilterOnEnrForkIdEnabled())) .rlpxAgent(rlpxAgent) .peerTable(peerTable) + .includeBootnodesOnPeerRefresh(config.getIncludeBootnodesOnPeerRefresh()) .build(); } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java index 5d0fee1845..11b5216a82 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java @@ -143,6 +143,7 @@ public class PeerDiscoveryController { private final AtomicBoolean peerTableIsDirty = new AtomicBoolean(false); private OptionalLong cleanTableTimerId = OptionalLong.empty(); private RecursivePeerRefreshState recursivePeerRefreshState; + private final boolean includeBootnodesOnPeerRefresh; private PeerDiscoveryController( final NodeKey nodeKey, @@ -159,7 +160,8 @@ public class PeerDiscoveryController { final MetricsSystem metricsSystem, final Optional> maybeCacheForEnrRequests, final boolean filterOnEnrForkId, - final RlpxAgent rlpxAgent) { + final RlpxAgent rlpxAgent, + final boolean includeBootnodesOnPeerRefresh) { this.timerUtil = timerUtil; this.nodeKey = nodeKey; this.localPeer = localPeer; @@ -173,6 +175,7 @@ public class PeerDiscoveryController { this.discoveryProtocolLogger = new DiscoveryProtocolLogger(metricsSystem); this.peerPermissions = new PeerDiscoveryPermissions(localPeer, peerPermissions); this.rlpxAgent = rlpxAgent; + this.includeBootnodesOnPeerRefresh = includeBootnodesOnPeerRefresh; metricsSystem.createIntegerGauge( BesuMetricCategory.NETWORK, @@ -483,7 +486,17 @@ public class PeerDiscoveryController { */ private void refreshTable() { final Bytes target = Peer.randomId(); + final List initialPeers = peerTable.nearestBondedPeers(Peer.randomId(), 16); + if (includeBootnodesOnPeerRefresh) { + bootstrapNodes.stream() + .filter(p -> p.getStatus() != PeerDiscoveryStatus.BONDED) + .forEach(p -> p.setStatus(PeerDiscoveryStatus.KNOWN)); + + // If configured to retry bootnodes during peer table refresh, include them + // in the initial peers list. + initialPeers.addAll(bootstrapNodes); + } recursivePeerRefreshState.start(initialPeers, target); lastRefreshTime = System.currentTimeMillis(); } @@ -816,6 +829,7 @@ public class PeerDiscoveryController { private long cleanPeerTableIntervalMs = MILLISECONDS.convert(1, TimeUnit.MINUTES); private final List bootstrapNodes = new ArrayList<>(); private PeerTable peerTable; + private boolean includeBootnodesOnPeerRefresh = true; // Required dependencies private NodeKey nodeKey; @@ -849,7 +863,8 @@ public class PeerDiscoveryController { metricsSystem, Optional.of(cachedEnrRequests), filterOnEnrForkId, - rlpxAgent); + rlpxAgent, + includeBootnodesOnPeerRefresh); } private void validate() { @@ -953,5 +968,10 @@ public class PeerDiscoveryController { this.rlpxAgent = rlpxAgent; return this; } + + public Builder includeBootnodesOnPeerRefresh(final boolean includeBootnodesOnPeerRefresh) { + this.includeBootnodesOnPeerRefresh = includeBootnodesOnPeerRefresh; + return this; + } } } diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index 11db6b8eee..426c1084db 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -194,6 +194,7 @@ dependencies { referenceTestImplementation project(path: ':config') referenceTestImplementation project(path: ':datatypes') referenceTestImplementation project(path: ':ethereum:core') + referenceTestImplementation project(path: ':metrics:core') referenceTestImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts') referenceTestImplementation project(path: ':ethereum:rlp') referenceTestImplementation project(path: ':ethereum:rlp', configuration: 'testSupportArtifacts') @@ -203,7 +204,7 @@ dependencies { referenceTestImplementation project(path: ':testutil') referenceTestImplementation project(path: ':util') // the following will be resolved via custom ivy repository declared in root build.gradle - referenceTestImplementation 'ethereum:execution-spec-tests:2.1.1:fixtures@tar.gz' + referenceTestImplementation 'ethereum:execution-spec-tests:3.0.0:fixtures_stable@tar.gz' referenceTestImplementation 'com.fasterxml.jackson.core:jackson-databind' referenceTestImplementation 'com.google.guava:guava' referenceTestImplementation 'io.tmio:tuweni-bytes' diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java index 1048f6238a..4645abdfcf 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java @@ -165,10 +165,6 @@ public class BlockchainReferenceTestCaseSpec { @JsonProperty("nonce") final String nonce, @JsonProperty("withdrawalsRoot") final String withdrawalsRoot, @JsonProperty("requestsRoot") final String requestsRoot, - @JsonProperty("dataGasUsed") - final String dataGasUsed, // TODO: remove once reference tests have been updated - @JsonProperty("excessDataGas") - final String excessDataGas, // TODO: remove once reference tests have been updated @JsonProperty("blobGasUsed") final String blobGasUsed, @JsonProperty("excessBlobGas") final String excessBlobGas, @JsonProperty("parentBeaconBlockRoot") final String parentBeaconBlockRoot, @@ -195,12 +191,8 @@ public class BlockchainReferenceTestCaseSpec { Hash.fromHexString(mixHash), // mixHash Bytes.fromHexStringLenient(nonce).toLong(), withdrawalsRoot != null ? Hash.fromHexString(withdrawalsRoot) : null, - dataGasUsed != null - ? Long.decode(dataGasUsed) - : blobGasUsed != null ? Long.decode(blobGasUsed) : 0, - excessDataGas != null - ? BlobGas.fromHexString(excessDataGas) - : excessBlobGas != null ? BlobGas.fromHexString(excessBlobGas) : null, + blobGasUsed != null ? Long.decode(blobGasUsed) : 0, + excessBlobGas != null ? BlobGas.fromHexString(excessBlobGas) : null, parentBeaconBlockRoot != null ? Bytes32.fromHexString(parentBeaconBlockRoot) : null, requestsRoot != null ? Hash.fromHexString(requestsRoot) : null, new BlockHeaderFunctions() { diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java index d9ed46c336..ddf6ac2eb1 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; +import java.util.List; import java.util.function.Supplier; public class GeneralStateTestCaseEipSpec { @@ -33,7 +34,7 @@ public class GeneralStateTestCaseEipSpec { // anything // is run, which isn't friendly and 2) this makes it harder to parallelize this step. Anyway, this // is why this is a supplier: calling get() actually does the signing. - private final Supplier transactionSupplier; + private final List> transactionSuppliers; private final ReferenceTestWorldState initialWorldState; @@ -51,7 +52,7 @@ public class GeneralStateTestCaseEipSpec { GeneralStateTestCaseEipSpec( final String fork, - final Supplier transactionSupplier, + final List> transactionSuppliers, final ReferenceTestWorldState initialWorldState, final Hash expectedRootHash, final Hash expectedLogsHash, @@ -61,7 +62,7 @@ public class GeneralStateTestCaseEipSpec { final int valueIndex, final String expectException) { this.fork = fork; - this.transactionSupplier = transactionSupplier; + this.transactionSuppliers = transactionSuppliers; this.initialWorldState = initialWorldState; this.expectedRootHash = expectedRootHash; this.expectedLogsHash = expectedLogsHash; @@ -88,9 +89,13 @@ public class GeneralStateTestCaseEipSpec { return expectedLogsHash; } - public Transaction getTransaction() { + public int getTransactionsCount() { + return transactionSuppliers.size(); + } + + public Transaction getTransaction(final int txIndex) { try { - return transactionSupplier.get(); + return transactionSuppliers.get(txIndex).get(); } catch (RuntimeException re) { // some tests specify invalid transactions. We throw exceptions in // GeneralStateTests but they are encoded in BlockchainTests, so we diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java index affc6e4601..394cb9a269 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java @@ -72,11 +72,12 @@ public class GeneralStateTestCaseSpec { .stateRoot(p.rootHash) .blockHeaderFunctions(MAINNET_FUNCTIONS) .buildBlockHeader(); - final Supplier txSupplier = () -> versionedTransaction.get(p.indexes); + final List> txSupplierList = + List.of(() -> versionedTransaction.get(p.indexes)); specs.add( new GeneralStateTestCaseEipSpec( eip, - txSupplier, + txSupplierList, initialWorldState, p.rootHash, p.logsHash, diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java index 8ac419f7a6..5dfb12f1d6 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.precompile.KZGPointEvalPrecompiledContract; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; import java.util.Arrays; @@ -130,7 +131,9 @@ public class ReferenceTestProtocolSchedules { false, EvmConfiguration.DEFAULT, MiningParameters.MINING_DISABLED, - new BadBlockManager()) + new BadBlockManager(), + false, + new NoOpMetricsSystem()) .createProtocolSchedule(); } diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java index 9748d2aa03..a36d380432 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java @@ -103,7 +103,6 @@ public class StateTestVersionedTransaction { @JsonDeserialize(using = StateTestAccessListDeserializer.class) @JsonProperty("accessLists") final List> maybeAccessLists, @JsonProperty("maxFeePerBlobGas") final String maxFeePerBlobGas, - @JsonProperty("maxFeePerDataGas") final String maxFeePerDataGas, @JsonProperty("blobVersionedHashes") final List blobVersionedHashes) { this.nonce = Bytes.fromHexStringLenient(nonce).toLong(); @@ -123,9 +122,7 @@ public class StateTestVersionedTransaction { this.payloads = parseArray(data, Bytes::fromHexString); this.maybeAccessLists = Optional.ofNullable(maybeAccessLists); this.maxFeePerBlobGas = - Optional.ofNullable(maxFeePerBlobGas == null ? maxFeePerDataGas : maxFeePerBlobGas) - .map(Wei::fromHexString) - .orElse(null); + Optional.ofNullable(maxFeePerBlobGas).map(Wei::fromHexString).orElse(null); this.blobVersionedHashes = blobVersionedHashes; } diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/mainnet/DifficultyCalculatorTests.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/mainnet/DifficultyCalculatorTests.java index 6377b1ae3b..00733de1f8 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/mainnet/DifficultyCalculatorTests.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/mainnet/DifficultyCalculatorTests.java @@ -18,6 +18,7 @@ package org.hyperledger.besu.ethereum.mainnet; import static org.assertj.core.api.Assertions.assertThat; +import org.checkerframework.checker.units.qual.N; import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.JsonUtil; import org.hyperledger.besu.config.StubGenesisConfigOptions; @@ -43,6 +44,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.io.Resources; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -59,64 +61,64 @@ public class DifficultyCalculatorTests { MainnetProtocolSchedule.fromConfig( GenesisConfigFile.mainnet() .getConfigOptions(postMergeOverrides), - EvmConfiguration.DEFAULT, MiningParameters.newDefault(), new BadBlockManager())), + EvmConfiguration.DEFAULT, MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem())), Arguments.of( "/DifficultyTests/dfGrayGlacier/difficultyGrayGlacierForkBlock.json", MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions().grayGlacierBlock(15050000), MiningParameters.newDefault(), new BadBlockManager()) + new StubGenesisConfigOptions().grayGlacierBlock(15050000), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfGrayGlacier/difficultyGrayGlacierTimeDiff1.json", MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions().grayGlacierBlock(15050000), MiningParameters.newDefault(), new BadBlockManager()) + new StubGenesisConfigOptions().grayGlacierBlock(15050000), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfGrayGlacier/difficultyGrayGlacierTimeDiff2.json", MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions().grayGlacierBlock(15050000), MiningParameters.newDefault(), new BadBlockManager()) + new StubGenesisConfigOptions().grayGlacierBlock(15050000), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfArrowGlacier/difficultyArrowGlacierForkBlock.json", MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions().arrowGlacierBlock(13773000), MiningParameters.newDefault(), new BadBlockManager()) + new StubGenesisConfigOptions().arrowGlacierBlock(13773000), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfArrowGlacier/difficultyArrowGlacierTimeDiff1.json", MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions().arrowGlacierBlock(13773000), MiningParameters.newDefault(), new BadBlockManager()) + new StubGenesisConfigOptions().arrowGlacierBlock(13773000), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfArrowGlacier/difficultyArrowGlacierTimeDiff2.json", MainnetProtocolSchedule.fromConfig( - new StubGenesisConfigOptions().arrowGlacierBlock(13773000), MiningParameters.newDefault(), new BadBlockManager()) + new StubGenesisConfigOptions().arrowGlacierBlock(13773000), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfByzantium/difficultyByzantium.json", - MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().byzantiumBlock(0), MiningParameters.newDefault(), new BadBlockManager()) + MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().byzantiumBlock(0), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfConstantinople/difficultyConstantinople.json", - MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().constantinopleBlock(0), MiningParameters.newDefault(), new BadBlockManager()) + MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().constantinopleBlock(0), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfEIP2384/difficultyEIP2384.json", - MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0), MiningParameters.newDefault(), new BadBlockManager()) + MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfEIP2384/difficultyEIP2384_random.json", - MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0), MiningParameters.newDefault(), new BadBlockManager()) + MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfEIP2384/difficultyEIP2384_random_to20M.json", - MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0), MiningParameters.newDefault(), new BadBlockManager()) + MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfFrontier/difficultyFrontier.json", - MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions(), MiningParameters.newDefault(), new BadBlockManager()) + MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions(), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) ), Arguments.of( "/DifficultyTests/dfHomestead/difficultyHomestead.json", - MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().homesteadBlock(0), MiningParameters.newDefault(), new BadBlockManager()) + MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().homesteadBlock(0), MiningParameters.newDefault(), new BadBlockManager(), false, new NoOpMetricsSystem()) )); } diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java index f948a5b24e..18d574adc1 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java @@ -120,7 +120,7 @@ public class GeneralStateReferenceTestTools { public static void executeTest(final GeneralStateTestCaseEipSpec spec) { final BlockHeader blockHeader = spec.getBlockHeader(); final ReferenceTestWorldState initialWorldState = spec.getInitialWorldState(); - final Transaction transaction = spec.getTransaction(); + final Transaction transaction = spec.getTransaction(0); ProtocolSpec protocolSpec = protocolSpec(spec.getFork()); BlockchainReferenceTestTools.verifyJournaledEVMAccountCompatability(initialWorldState, protocolSpec); diff --git a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java index fd39ef7f44..32d4e0bab1 100644 --- a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java +++ b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java @@ -161,7 +161,12 @@ public class RetestethContext { JsonUtil.getObjectNode(genesisConfig, "config").get()); protocolSchedule = MainnetProtocolSchedule.fromConfig( - jsonGenesisConfigOptions, EvmConfiguration.DEFAULT, miningParameters, badBlockManager); + jsonGenesisConfigOptions, + EvmConfiguration.DEFAULT, + miningParameters, + badBlockManager, + false, + new NoOpMetricsSystem()); if ("NoReward".equalsIgnoreCase(sealEngine)) { protocolSchedule = new NoRewardProtocolScheduleWrapper(protocolSchedule, badBlockManager); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 2d9be8d312..06ef2da253 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -240,8 +240,8 @@ public class MainnetEVMs { registry.put(new CodeSizeOperation(gasCalculator)); registry.put(new CodeCopyOperation(gasCalculator)); registry.put(new GasPriceOperation(gasCalculator)); - registry.put(new ExtCodeCopyOperation(gasCalculator)); - registry.put(new ExtCodeSizeOperation(gasCalculator)); + registry.put(new ExtCodeCopyOperation(gasCalculator, false)); + registry.put(new ExtCodeSizeOperation(gasCalculator, false)); registry.put(new BlockHashOperation(gasCalculator)); registry.put(new CoinbaseOperation(gasCalculator)); registry.put(new TimestampOperation(gasCalculator)); @@ -478,7 +478,7 @@ public class MainnetEVMs { registry.put(new SarOperation(gasCalculator)); registry.put(new ShlOperation(gasCalculator)); registry.put(new ShrOperation(gasCalculator)); - registry.put(new ExtCodeHashOperation(gasCalculator)); + registry.put(new ExtCodeHashOperation(gasCalculator, false)); } /** @@ -1114,6 +1114,11 @@ public class MainnetEVMs { registry.put(new SwapNOperation(gasCalculator)); registry.put(new ExchangeOperation(gasCalculator)); + // EIP-3540 EOF Aware EXTCODE* operations + registry.put(new ExtCodeCopyOperation(gasCalculator, true)); + registry.put(new ExtCodeHashOperation(gasCalculator, true)); + registry.put(new ExtCodeSizeOperation(gasCalculator, true)); + // EIP-4200 relative jump registry.put(new RelativeJumpOperation(gasCalculator)); registry.put(new RelativeJumpIfOperation(gasCalculator)); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/account/AuthorizedCodeAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/account/AuthorizedCodeAccount.java new file mode 100644 index 0000000000..46acac74f9 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/account/AuthorizedCodeAccount.java @@ -0,0 +1,100 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.account; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; + +import java.util.NavigableMap; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt256; + +/** Wraps an EOA account and includes authorized code to be run on behalf of it. */ +public class AuthorizedCodeAccount implements Account { + private final Account wrappedAccount; + private final Bytes authorizedCode; + + /** The hash of the authorized code. */ + protected Hash codeHash = null; + + /** + * Creates a new AuthorizedCodeAccount. + * + * @param wrappedAccount the account that has authorized code to be loaded into it. + * @param authorizedCode the authorized code. + */ + public AuthorizedCodeAccount(final Account wrappedAccount, final Bytes authorizedCode) { + this.wrappedAccount = wrappedAccount; + this.authorizedCode = authorizedCode; + } + + @Override + public Address getAddress() { + return wrappedAccount.getAddress(); + } + + @Override + public boolean isStorageEmpty() { + return wrappedAccount.isStorageEmpty(); + } + + @Override + public Hash getAddressHash() { + return wrappedAccount.getAddressHash(); + } + + @Override + public long getNonce() { + return wrappedAccount.getNonce(); + } + + @Override + public Wei getBalance() { + return wrappedAccount.getBalance(); + } + + @Override + public Bytes getCode() { + return authorizedCode; + } + + @Override + public Hash getCodeHash() { + if (codeHash == null) { + codeHash = authorizedCode.equals(Bytes.EMPTY) ? Hash.EMPTY : Hash.hash(authorizedCode); + } + + return codeHash; + } + + @Override + public UInt256 getStorageValue(final UInt256 key) { + return wrappedAccount.getStorageValue(key); + } + + @Override + public UInt256 getOriginalStorageValue(final UInt256 key) { + return wrappedAccount.getOriginalStorageValue(key); + } + + @Override + public NavigableMap storageEntriesFrom( + final Bytes32 startKeyHash, final int limit) { + return wrappedAccount.storageEntriesFrom(startKeyHash, limit); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/account/MutableAuthorizedCodeAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/account/MutableAuthorizedCodeAccount.java new file mode 100644 index 0000000000..6d4e30a9c2 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/account/MutableAuthorizedCodeAccount.java @@ -0,0 +1,138 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.account; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; + +import java.util.Map; +import java.util.NavigableMap; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt256; + +/** Wraps a mutable EOA account and includes authorized code to be run on behalf of it. */ +public class MutableAuthorizedCodeAccount implements MutableAccount { + + private final MutableAccount wrappedAccount; + private final Bytes authorizedCode; + + /** The hash of the authorized code. */ + protected Hash codeHash = null; + + /** + * Creates a new MutableAuthorizedCodeAccount. + * + * @param wrappedAccount the account that has authorized code to be loaded into it. + * @param authorizedCode the authorized code. + */ + public MutableAuthorizedCodeAccount( + final MutableAccount wrappedAccount, final Bytes authorizedCode) { + this.wrappedAccount = wrappedAccount; + this.authorizedCode = authorizedCode; + } + + @Override + public Address getAddress() { + return wrappedAccount.getAddress(); + } + + @Override + public boolean isStorageEmpty() { + return wrappedAccount.isStorageEmpty(); + } + + @Override + public Hash getAddressHash() { + return wrappedAccount.getAddressHash(); + } + + @Override + public long getNonce() { + return wrappedAccount.getNonce(); + } + + @Override + public Wei getBalance() { + return wrappedAccount.getBalance(); + } + + @Override + public Bytes getCode() { + return authorizedCode; + } + + @Override + public Hash getCodeHash() { + if (codeHash == null) { + codeHash = authorizedCode.equals(Bytes.EMPTY) ? Hash.EMPTY : Hash.hash(authorizedCode); + } + + return codeHash; + } + + @Override + public UInt256 getStorageValue(final UInt256 key) { + return wrappedAccount.getStorageValue(key); + } + + @Override + public UInt256 getOriginalStorageValue(final UInt256 key) { + return wrappedAccount.getOriginalStorageValue(key); + } + + @Override + public NavigableMap storageEntriesFrom( + final Bytes32 startKeyHash, final int limit) { + return wrappedAccount.storageEntriesFrom(startKeyHash, limit); + } + + @Override + public void setNonce(final long value) { + wrappedAccount.setNonce(value); + } + + @Override + public void setBalance(final Wei value) { + wrappedAccount.setBalance(value); + } + + @Override + public void setCode(final Bytes code) { + throw new RuntimeException("Cannot set code on an AuthorizedCodeAccount"); + } + + @Override + public void setStorageValue(final UInt256 key, final UInt256 value) { + wrappedAccount.setStorageValue(key, value); + } + + @Override + public void clearStorage() { + wrappedAccount.clearStorage(); + } + + @Override + public Map getUpdatedStorage() { + return wrappedAccount.getUpdatedStorage(); + } + + @Override + public void becomeImmutable() { + wrappedAccount.becomeImmutable(); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java index f52e788bab..c64ca3744f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.worldstate.AuthorizedCodeService; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.Collection; @@ -34,6 +35,8 @@ public class SimpleWorld implements WorldUpdater { /** The Accounts. */ Map accounts = new HashMap<>(); + private AuthorizedCodeService authorizedCodeService; + /** Instantiates a new Simple world. */ public SimpleWorld() { this(null); @@ -46,6 +49,7 @@ public class SimpleWorld implements WorldUpdater { */ public SimpleWorld(final SimpleWorld parent) { this.parent = parent; + this.authorizedCodeService = new AuthorizedCodeService(); } @Override @@ -56,11 +60,11 @@ public class SimpleWorld implements WorldUpdater { @Override public Account get(final Address address) { if (accounts.containsKey(address)) { - return accounts.get(address); + return authorizedCodeService.processAccount(this, accounts.get(address), address); } else if (parent != null) { - return parent.get(address); + return authorizedCodeService.processAccount(this, parent.get(address), address); } else { - return null; + return authorizedCodeService.processAccount(this, null, address); } } @@ -71,14 +75,14 @@ public class SimpleWorld implements WorldUpdater { } SimpleAccount account = new SimpleAccount(address, nonce, balance); accounts.put(address, account); - return account; + return authorizedCodeService.processMutableAccount(this, account, address); } @Override public MutableAccount getAccount(final Address address) { SimpleAccount account = accounts.get(address); if (account != null) { - return account; + return authorizedCodeService.processMutableAccount(this, account, address); } Account parentAccount = parent == null ? null : parent.getAccount(address); if (parentAccount != null) { @@ -90,9 +94,9 @@ public class SimpleWorld implements WorldUpdater { parentAccount.getBalance(), parentAccount.getCode()); accounts.put(address, account); - return account; + return authorizedCodeService.processMutableAccount(this, account, address); } - return null; + return authorizedCodeService.processMutableAccount(this, null, address); } @Override @@ -132,4 +136,9 @@ public class SimpleWorld implements WorldUpdater { public Optional parentUpdater() { return Optional.ofNullable(parent); } + + @Override + public void setAuthorizedCodeService(final AuthorizedCodeService authorizedCodeService) { + this.authorizedCodeService = authorizedCodeService; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index e01c915510..cf77619229 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -33,6 +33,7 @@ import org.hyperledger.besu.evm.internal.UnderflowException; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.evm.operation.Operation; +import org.hyperledger.besu.evm.worldstate.AuthorizedCodeService; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.ArrayDeque; @@ -201,6 +202,7 @@ public class MessageFrame { // Global data fields. private final WorldUpdater worldUpdater; + private final AuthorizedCodeService authorizedCodeService; // Metadata fields. private final Type type; @@ -270,7 +272,8 @@ public class MessageFrame { final Consumer completer, final Map contextVariables, final Optional revertReason, - final TxValues txValues) { + final TxValues txValues, + final AuthorizedCodeService authorizedCodeService) { this.txValues = txValues; this.type = type; @@ -290,6 +293,7 @@ public class MessageFrame { this.completer = completer; this.contextVariables = contextVariables; this.revertReason = revertReason; + this.authorizedCodeService = authorizedCodeService; this.undoMark = txValues.transientStorage().mark(); } @@ -423,6 +427,15 @@ public class MessageFrame { return returnData; } + /** + * Return the authorized account service. + * + * @return the authorized account service + */ + public AuthorizedCodeService getAuthorizedCodeService() { + return authorizedCodeService; + } + /** * Set the return data. * @@ -1347,6 +1360,7 @@ public class MessageFrame { private Optional reason = Optional.empty(); private Set

accessListWarmAddresses = emptySet(); private Multimap accessListWarmStorage = HashMultimap.create(); + private AuthorizedCodeService authorizedCodeService; private Optional> versionedHashes = Optional.empty(); @@ -1631,6 +1645,17 @@ public class MessageFrame { return this; } + /** + * Sets authorized account service. + * + * @param authorizedCodeService the authorized account service + * @return the builder + */ + public Builder authorizedCodeService(final AuthorizedCodeService authorizedCodeService) { + this.authorizedCodeService = authorizedCodeService; + return this; + } + private void validate() { if (parentMessageFrame == null) { checkState(worldUpdater != null, "Missing message frame world updater"); @@ -1664,6 +1689,11 @@ public class MessageFrame { WorldUpdater updater; boolean newStatic; TxValues newTxValues; + + if (authorizedCodeService == null) { + authorizedCodeService = new AuthorizedCodeService(); + } + if (parentMessageFrame == null) { newTxValues = new TxValues( @@ -1686,10 +1716,12 @@ public class MessageFrame { newStatic = isStatic; } else { newTxValues = parentMessageFrame.txValues; - updater = parentMessageFrame.worldUpdater.updater(); + updater = parentMessageFrame.getWorldUpdater().updater(); newStatic = isStatic || parentMessageFrame.isStatic; } + updater.setAuthorizedCodeService(authorizedCodeService); + MessageFrame messageFrame = new MessageFrame( type, @@ -1706,7 +1738,8 @@ public class MessageFrame { completer, contextVariables == null ? Map.of() : contextVariables, reason, - newTxValues); + newTxValues, + authorizedCodeService); newTxValues.messageFrameStack().addFirst(messageFrame); messageFrame.warmUpAddress(sender); messageFrame.warmUpAddress(contract); 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 2e1728b234..0af023851c 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 @@ -645,4 +645,14 @@ public interface GasCalculator { default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) { return 0L; } + + /** + * Returns the upfront gas cost for EIP 7702 operation. + * + * @param authorizationListLength The length of the authorization list + * @return the gas cost + */ + default long setCodeListGasCost(final int authorizationListLength) { + return 0L; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java index e2789bba33..0c223d780b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java @@ -28,6 +28,8 @@ import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2; */ public class PragueGasCalculator extends CancunGasCalculator { + static final long PER_CONTRACT_CODE_BASE_COST = 2500L; + /** Instantiates a new Prague Gas Calculator. */ public PragueGasCalculator() { this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19]); @@ -41,4 +43,9 @@ public class PragueGasCalculator extends CancunGasCalculator { protected PragueGasCalculator(final int maxPrecompile) { super(maxPrecompile); } + + @Override + public long setCodeListGasCost(final int authorizationListLength) { + return PER_CONTRACT_CODE_BASE_COST * authorizationListLength; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java index 8a82262204..762030bf3b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCallOperation.java @@ -230,6 +230,7 @@ public abstract class AbstractCallOperation extends AbstractOperation { .code(code) .isStatic(isStatic(frame)) .completer(child -> complete(frame, child)) + .authorizedCodeService(frame.getAuthorizedCodeService()) .build(); // see note in stack depth check about incrementing cost frame.incrementRemainingGas(cost); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java index f20c2914a0..16ea218f9c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java @@ -195,6 +195,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation { .apparentValue(value) .code(code) .completer(child -> complete(parent, child, evm)) + .authorizedCodeService(parent.getAuthorizedCodeService()) .build(); parent.setState(MessageFrame.State.CODE_SUSPENDED); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java index 43d7e343ff..e19b6cd8a2 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java @@ -180,6 +180,7 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation { .code(code) .isStatic(isStatic(frame)) .completer(child -> complete(frame, child)) + .authorizedCodeService(frame.getAuthorizedCodeService()) .build(); frame.setState(MessageFrame.State.CODE_SUSPENDED); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java index 37a92ffc6e..2f1c139182 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java @@ -34,13 +34,26 @@ public class ExtCodeCopyOperation extends AbstractOperation { /** This is the "code" legacy contracts see when copying code from an EOF contract. */ public static final Bytes EOF_REPLACEMENT_CODE = Bytes.fromHexString("0xef00"); + private final boolean enableEIP3540; + /** * Instantiates a new Ext code copy operation. * * @param gasCalculator the gas calculator */ public ExtCodeCopyOperation(final GasCalculator gasCalculator) { + this(gasCalculator, false); + } + + /** + * Instantiates a new Ext code copy operation. + * + * @param gasCalculator the gas calculator + * @param enableEIP3540 enable EIP-3540 semantics (don't copy EOF) + */ + public ExtCodeCopyOperation(final GasCalculator gasCalculator, final boolean enableEIP3540) { super(0x3C, "EXTCODECOPY", 4, 0, gasCalculator); + this.enableEIP3540 = enableEIP3540; } /** @@ -82,7 +95,10 @@ public class ExtCodeCopyOperation extends AbstractOperation { final Account account = frame.getWorldUpdater().get(address); final Bytes code = account != null ? account.getCode() : Bytes.EMPTY; - if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) { + if (enableEIP3540 + && code.size() >= 2 + && code.get(0) == EOFLayout.EOF_PREFIX_BYTE + && code.get(1) == 0) { frame.writeMemory(memOffset, sourceOffset, numBytes, EOF_REPLACEMENT_CODE); } else { frame.writeMemory(memOffset, sourceOffset, numBytes, code); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java index 953ddfb04d..c08331b006 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java @@ -34,13 +34,26 @@ public class ExtCodeHashOperation extends AbstractOperation { // // 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5 static final Hash EOF_REPLACEMENT_HASH = Hash.hash(ExtCodeCopyOperation.EOF_REPLACEMENT_CODE); + private final boolean enableEIP3540; + /** * Instantiates a new Ext code hash operation. * * @param gasCalculator the gas calculator */ public ExtCodeHashOperation(final GasCalculator gasCalculator) { + this(gasCalculator, false); + } + + /** + * Instantiates a new Ext code copy operation. + * + * @param gasCalculator the gas calculator + * @param enableEIP3540 enable EIP-3540 semantics (don't copy EOF) + */ + public ExtCodeHashOperation(final GasCalculator gasCalculator, final boolean enableEIP3540) { super(0x3F, "EXTCODEHASH", 1, 1, gasCalculator); + this.enableEIP3540 = enableEIP3540; } /** @@ -71,7 +84,10 @@ public class ExtCodeHashOperation extends AbstractOperation { frame.pushStackItem(Bytes.EMPTY); } else { final Bytes code = account.getCode(); - if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) { + if (enableEIP3540 + && code.size() >= 2 + && code.get(0) == EOFLayout.EOF_PREFIX_BYTE + && code.get(1) == 0) { frame.pushStackItem(EOF_REPLACEMENT_HASH); } else { frame.pushStackItem(account.getCodeHash()); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java index 95e5acc6ff..1779175f15 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java @@ -32,13 +32,26 @@ public class ExtCodeSizeOperation extends AbstractOperation { static final Bytes EOF_SIZE = Bytes.of(2); + private final boolean enableEIP3540; + /** * Instantiates a new Ext code size operation. * * @param gasCalculator the gas calculator */ public ExtCodeSizeOperation(final GasCalculator gasCalculator) { + this(gasCalculator, false); + } + + /** + * Instantiates a new Ext code size operation. + * + * @param gasCalculator the gas calculator + * @param enableEIP3540 enable EIP-3540 semantics (EOF is size 2) + */ + public ExtCodeSizeOperation(final GasCalculator gasCalculator, final boolean enableEIP3540) { super(0x3B, "EXTCODESIZE", 1, 1, gasCalculator); + this.enableEIP3540 = enableEIP3540; } /** @@ -70,7 +83,10 @@ public class ExtCodeSizeOperation extends AbstractOperation { codeSize = Bytes.EMPTY; } else { final Bytes code = account.getCode(); - if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) { + if (enableEIP3540 + && code.size() >= 2 + && code.get(0) == EOFLayout.EOF_PREFIX_BYTE + && code.get(1) == 0) { codeSize = EOF_SIZE; } else { codeSize = Words.intBytes(code.size()); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AbstractAltBnPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AbstractAltBnPrecompiledContract.java index 84ce6c6cf9..5f8e4b0a8c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AbstractAltBnPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AbstractAltBnPrecompiledContract.java @@ -19,7 +19,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings; +import org.hyperledger.besu.nativelib.gnark.LibGnarkEIP196; import java.util.Optional; import javax.annotation.Nonnull; @@ -49,7 +49,7 @@ public abstract class AbstractAltBnPrecompiledContract extends AbstractPrecompil */ public static boolean maybeEnableNative() { try { - useNative = LibEthPairings.ENABLED; + useNative = LibGnarkEIP196.ENABLED; } catch (UnsatisfiedLinkError | NoClassDefFoundError ule) { LOG.info("altbn128 native precompile not available: {}", ule.getMessage()); useNative = false; @@ -72,7 +72,7 @@ public abstract class AbstractAltBnPrecompiledContract extends AbstractPrecompil } private final byte operationId; - private final int inputLen; + private final int inputLimit; /** * Instantiates a new Abstract alt bn precompiled contract. @@ -89,9 +89,9 @@ public abstract class AbstractAltBnPrecompiledContract extends AbstractPrecompil final int inputLen) { super(name, gasCalculator); this.operationId = operationId; - this.inputLen = inputLen + 1; + this.inputLimit = inputLen + 1; - if (!LibEthPairings.ENABLED) { + if (!LibGnarkEIP196.ENABLED) { LOG.info("Native alt bn128 not available"); } } @@ -106,16 +106,16 @@ public abstract class AbstractAltBnPrecompiledContract extends AbstractPrecompil @Nonnull public PrecompileContractResult computeNative( final @Nonnull Bytes input, final MessageFrame messageFrame) { - final byte[] result = new byte[LibEthPairings.EIP196_PREALLOCATE_FOR_RESULT_BYTES]; - final byte[] error = new byte[LibEthPairings.EIP2537_PREALLOCATE_FOR_ERROR_BYTES]; + final byte[] result = new byte[LibGnarkEIP196.EIP196_PREALLOCATE_FOR_RESULT_BYTES]; + final byte[] error = new byte[LibGnarkEIP196.EIP196_PREALLOCATE_FOR_ERROR_BYTES]; final IntByReference o_len = - new IntByReference(LibEthPairings.EIP196_PREALLOCATE_FOR_RESULT_BYTES); + new IntByReference(LibGnarkEIP196.EIP196_PREALLOCATE_FOR_RESULT_BYTES); final IntByReference err_len = - new IntByReference(LibEthPairings.EIP2537_PREALLOCATE_FOR_ERROR_BYTES); - final int inputSize = Math.min(inputLen, input.size()); + new IntByReference(LibGnarkEIP196.EIP196_PREALLOCATE_FOR_ERROR_BYTES); + final int inputSize = Math.min(inputLimit, input.size()); final int errorNo = - LibEthPairings.eip196_perform_operation( + LibGnarkEIP196.eip196_perform_operation( operationId, input.slice(0, inputSize).toArrayUnsafe(), inputSize, @@ -123,6 +123,7 @@ public abstract class AbstractAltBnPrecompiledContract extends AbstractPrecompil o_len, error, err_len); + if (errorNo == 0) { return PrecompileContractResult.success(Bytes.wrap(result, 0, o_len.getValue())); } else { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128AddPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128AddPrecompiledContract.java index 026a78c4a2..8aaa276586 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128AddPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128AddPrecompiledContract.java @@ -19,7 +19,7 @@ import org.hyperledger.besu.crypto.altbn128.Fq; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings; +import org.hyperledger.besu.nativelib.gnark.LibGnarkEIP196; import java.math.BigInteger; import java.util.Arrays; @@ -40,7 +40,7 @@ public class AltBN128AddPrecompiledContract extends AbstractAltBnPrecompiledCont super( "AltBN128Add", gasCalculator, - LibEthPairings.EIP196_ADD_OPERATION_RAW_VALUE, + LibGnarkEIP196.EIP196_ADD_OPERATION_RAW_VALUE, PARAMETER_LENGTH); this.gasCost = gasCost; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128MulPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128MulPrecompiledContract.java index 73b824f763..25399d9e71 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128MulPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128MulPrecompiledContract.java @@ -19,7 +19,7 @@ import org.hyperledger.besu.crypto.altbn128.Fq; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings; +import org.hyperledger.besu.nativelib.gnark.LibGnarkEIP196; import java.math.BigInteger; import java.util.Arrays; @@ -44,7 +44,7 @@ public class AltBN128MulPrecompiledContract extends AbstractAltBnPrecompiledCont super( "AltBN128Mul", gasCalculator, - LibEthPairings.EIP196_MUL_OPERATION_RAW_VALUE, + LibGnarkEIP196.EIP196_MUL_OPERATION_RAW_VALUE, PARAMETER_LENGTH); this.gasCost = gasCost; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128PairingPrecompiledContract.java b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128PairingPrecompiledContract.java index 82496d4e31..e6cd892233 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128PairingPrecompiledContract.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/precompile/AltBN128PairingPrecompiledContract.java @@ -23,7 +23,7 @@ import org.hyperledger.besu.crypto.altbn128.Fq2; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.nativelib.bls12_381.LibEthPairings; +import org.hyperledger.besu.nativelib.gnark.LibGnarkEIP196; import java.math.BigInteger; import java.util.ArrayList; @@ -56,7 +56,7 @@ public class AltBN128PairingPrecompiledContract extends AbstractAltBnPrecompiled super( "AltBN128Pairing", gasCalculator, - LibEthPairings.EIP196_PAIR_OPERATION_RAW_VALUE, + LibGnarkEIP196.EIP196_PAIR_OPERATION_RAW_VALUE, Integer.MAX_VALUE / PARAMETER_LENGTH * PARAMETER_LENGTH); this.pairingGasCost = pairingGasCost; this.baseGasCost = baseGasCost; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java index 34e28bbb32..f139386118 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java @@ -16,6 +16,7 @@ package org.hyperledger.besu.evm.tracing; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.log.Log; @@ -85,6 +86,17 @@ public interface OperationTracer { */ default void traceStartTransaction(final WorldView worldView, final Transaction transaction) {} + /** + * Trace the end of a transaction just before mining reward. + * + * @param worldView an immutable view of the execution context + * @param tx the transaction that just concluded + * @param miningReward the reward that the mining beneficiary will receive. + */ + default void traceBeforeRewardTransaction( + final WorldView worldView, final Transaction tx, final Wei miningReward) {} + ; + /** * Trace the end of a transaction. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java index 9623eaf89e..00cd67ddb8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java @@ -43,6 +43,9 @@ public abstract class AbstractWorldUpdater> updatedAccounts = new ConcurrentHashMap<>(); @@ -59,6 +62,7 @@ public abstract class AbstractWorldUpdater account = new UpdateTrackingAccount<>(address); account.setNonce(nonce); account.setBalance(balance); - return track(account); + return authorizedCodeService.processMutableAccount(this, track(account), address); } @Override @@ -95,12 +99,12 @@ public abstract class AbstractWorldUpdater(origin)); + return authorizedCodeService.processMutableAccount( + this, track(new UpdateTrackingAccount<>(origin)), address); } } @@ -165,6 +170,11 @@ public abstract class AbstractWorldUpdater authorizedCode = new HashMap<>(); + + /** Creates a new AuthorizedCodeService. */ + public AuthorizedCodeService() {} + + /** + * Authorizes to load the code of authorizedCode into the authorizer account. + * + * @param authorizer the address that gives the authorization. + * @param authorizedCode the code which will be loaded. + */ + public void addAuthorizedCode(final Address authorizer, final Bytes authorizedCode) { + this.authorizedCode.put(authorizer, authorizedCode); + } + + /** + * Return all the authorities that have given their authorization to load the code of another + * account. + * + * @return the set of authorities. + */ + public Set
getAuthorities() { + return authorizedCode.keySet(); + } + + /** Resets all the authorized accounts. */ + public void resetAuthorities() { + authorizedCode.clear(); + } + + /** + * Checks if the provided address has set an authorized to load code into an EOA account. + * + * @param authority the address to check. + * @return {@code true} if the address has been authorized, {@code false} otherwise. + */ + public boolean hasAuthorizedCode(final Address authority) { + return authorizedCode.containsKey(authority); + } + + /** + * Processes the provided account, injecting the authorized code if authorized. + * + * @param worldUpdater the world updater to retrieve the code account. + * @param originalAccount the account to process. + * @param address the address of the account in case the provided account is null + * @return the processed account, containing the authorized code if authorized. + */ + public Account processAccount( + final WorldUpdater worldUpdater, final Account originalAccount, final Address address) { + if (!authorizedCode.containsKey(address)) { + return originalAccount; + } + + Account account = originalAccount; + if (account == null) { + account = worldUpdater.createAccount(address); + } + + return new AuthorizedCodeAccount(account, authorizedCode.get(address)); + } + + /** + * Processes the provided mutable account, injecting the authorized code if authorized. + * + * @param worldUpdater the world updater to retrieve the code account. + * @param originalAccount the mutable account to process. + * @param address the address of the account in case the provided account is null + * @return the processed mutable account, containing the authorized code if authorized. + */ + public MutableAccount processMutableAccount( + final WorldUpdater worldUpdater, + final MutableAccount originalAccount, + final Address address) { + if (!authorizedCode.containsKey(address)) { + return originalAccount; + } + + MutableAccount account = originalAccount; + if (account == null) { + account = worldUpdater.createAccount(address); + } + + return new MutableAuthorizedCodeAccount(account, authorizedCode.get(address)); + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java index 9987a5be75..2497ed348d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/JournaledUpdater.java @@ -41,6 +41,7 @@ public class JournaledUpdater implements WorldUpdater { final UndoMap accounts; final UndoSet
deleted; final long undoMark; + private AuthorizedCodeService authorizedCodeService; /** * Instantiates a new Stacked updater. @@ -66,6 +67,7 @@ public class JournaledUpdater implements WorldUpdater { "WorldUpdater must be a JournaledWorldUpdater or an AbstractWorldUpdater"); } undoMark = accounts.mark(); + this.authorizedCodeService = new AuthorizedCodeService(); } /** @@ -126,12 +128,18 @@ public class JournaledUpdater implements WorldUpdater { accounts.values().forEach(JournaledAccount::markTransactionBoundary); } + @Override + public void setAuthorizedCodeService(final AuthorizedCodeService authorizedCodeService) { + this.authorizedCodeService = authorizedCodeService; + } + @Override public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { JournaledAccount journaledAccount = new JournaledAccount(rootWorld.createAccount(address, nonce, balance)); accounts.put(address, journaledAccount); - return new JournaledAccount(journaledAccount); + return authorizedCodeService.processMutableAccount( + this, new JournaledAccount(journaledAccount), address); } @Override @@ -139,7 +147,7 @@ public class JournaledUpdater implements WorldUpdater { // We may have updated it already, so check that first. final JournaledAccount existing = accounts.get(address); if (existing != null) { - return existing; + return authorizedCodeService.processMutableAccount(this, existing, address); } if (deleted.contains(address)) { return null; @@ -148,11 +156,11 @@ public class JournaledUpdater implements WorldUpdater { // Otherwise, get it from our wrapped view and create a new update tracker. final MutableAccount origin = rootWorld.getAccount(address); if (origin == null) { - return null; + return authorizedCodeService.processMutableAccount(this, null, address); } else { var newAccount = new JournaledAccount(origin); accounts.put(address, newAccount); - return newAccount; + return authorizedCodeService.processMutableAccount(this, newAccount, address); } } @@ -169,12 +177,12 @@ public class JournaledUpdater implements WorldUpdater { public Account get(final Address address) { final MutableAccount existing = accounts.get(address); if (existing != null) { - return existing; + return authorizedCodeService.processAccount(this, existing, address); } if (deleted.contains(address)) { - return null; + return authorizedCodeService.processAccount(this, null, address); } - return rootWorld.get(address); + return authorizedCodeService.processAccount(this, rootWorld.get(address), address); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java index 4cbda73279..5144176e79 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java @@ -179,4 +179,12 @@ public interface WorldUpdater extends MutableWorldView { default void markTransactionBoundary() { // default is to ignore } + + /** + * Sets the {@link AuthorizedCodeService} associated with this {@link WorldUpdater}. + * + * @param authorizedCodeService the {@link AuthorizedCodeService} to associate with this {@link + * WorldUpdater} + */ + void setAuthorizedCodeService(AuthorizedCodeService authorizedCodeService); } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/BaseFeeOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/BaseFeeOperationTest.java similarity index 95% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/BaseFeeOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/BaseFeeOperationTest.java index ac816137af..13f0484988 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/BaseFeeOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/BaseFeeOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -26,8 +26,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.Words; -import org.hyperledger.besu.evm.operation.BaseFeeOperation; -import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.operation.Operation.OperationResult; import java.util.Optional; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/BlobHashOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/BlobHashOperationTest.java similarity index 96% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/BlobHashOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/BlobHashOperationTest.java index cebffeb910..9692c27625 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/BlobHashOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/BlobHashOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -25,8 +25,6 @@ import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; -import org.hyperledger.besu.evm.operation.BlobHashOperation; -import org.hyperledger.besu.evm.operation.Operation; import java.util.ArrayList; import java.util.Arrays; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/BlockHashOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/BlockHashOperationTest.java similarity index 97% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/BlockHashOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/BlockHashOperationTest.java index d19e94e0a6..3ce7e3c207 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/BlockHashOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/BlockHashOperationTest.java @@ -12,14 +12,13 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator; -import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.evm.testutils.FakeBlockValues; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/CallFOperationTest.java similarity index 93% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/CallFOperationTest.java index fe662893e1..9d7fa85d41 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CallFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/CallFOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode; @@ -24,8 +24,6 @@ import org.hyperledger.besu.evm.code.CodeSection; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.ReturnStack; -import org.hyperledger.besu.evm.operation.CallFOperation; -import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.apache.tuweni.bytes.Bytes; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ChainIdOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ChainIdOperationTest.java similarity index 96% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/ChainIdOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/ChainIdOperationTest.java index 969039266d..acae6249c4 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ChainIdOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ChainIdOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -20,7 +20,6 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; -import org.hyperledger.besu.evm.operation.ChainIdOperation; import org.hyperledger.besu.evm.operation.Operation.OperationResult; import java.util.List; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ConstantinopleSStoreOperationGasCostTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ConstantinopleSStoreOperationGasCostTest.java similarity index 98% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/ConstantinopleSStoreOperationGasCostTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/ConstantinopleSStoreOperationGasCostTest.java index 3cea9300e1..b8a254bbe4 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ConstantinopleSStoreOperationGasCostTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ConstantinopleSStoreOperationGasCostTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/Create2OperationTest.java similarity index 99% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/Create2OperationTest.java index 73a4abd34a..51e2863a69 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/Create2OperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID; @@ -35,7 +35,6 @@ import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.log.Log; -import org.hyperledger.besu.evm.operation.Create2Operation; import org.hyperledger.besu.evm.operation.Operation.OperationResult; import org.hyperledger.besu.evm.processor.ContractCreationProcessor; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/CreateOperationTest.java similarity index 99% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/CreateOperationTest.java index 4fa5a0fdd7..c10d8f3d6c 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/CreateOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID; @@ -34,7 +34,6 @@ import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.log.Log; -import org.hyperledger.besu.evm.operation.CreateOperation; import org.hyperledger.besu.evm.processor.ContractCreationProcessor; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.hyperledger.besu.evm.tracing.OperationTracer; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/DataCopyOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/DataCopyOperationTest.java similarity index 97% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/DataCopyOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/DataCopyOperationTest.java index 83d7f09e5a..af5922aa07 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/DataCopyOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/DataCopyOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assumptions.assumeThat; @@ -24,8 +24,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.operation.DataCopyOperation; -import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import java.util.Arrays; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/EofCreateOperationTest.java similarity index 99% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/EofCreateOperationTest.java index 86a0f8cce1..d5d1212dfb 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/EofCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/EofCreateOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCallOperationTest.java similarity index 98% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCallOperationTest.java index 946dd0b52e..c43893a543 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCallOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -29,8 +29,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.operation.AbstractExtCallOperation; -import org.hyperledger.besu.evm.operation.ExtCallOperation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.hyperledger.besu.evm.worldstate.WorldUpdater; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperationTest.java new file mode 100644 index 0000000000..b45824a92b --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperationTest.java @@ -0,0 +1,245 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; +import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; +import org.hyperledger.besu.evm.toy.ToyWorld; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.Arrays; +import java.util.Collection; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class ExtCodeCopyOperationTest { + + private static final Address REQUESTED_ADDRESS = Address.fromHexString("0x22222222"); + + private final ToyWorld toyWorld = new ToyWorld(); + private final WorldUpdater worldStateUpdater = toyWorld.updater(); + + @Mock EVM evm; + + static Collection extCodeCopyTestVector() { + return Arrays.asList( + new Object[][] { + { + "Copy after, no overlap", + Bytes.fromHexString("0123456789abcdef000000000000000000000000000000000000000000000000"), + 32, + 0, + 8, + Bytes.fromHexString( + "00000000000000000000000000000000000000000000000000000000000000000123456789abcdef"), + false, + 2609L + }, + { + "copy from uninitialized memory", + Bytes.EMPTY, + 0, + 24, + 16, + Bytes.fromHexString( + "0x000000000000000000000000000000000000000000000000000000000000000000"), + false, + 2606L + }, + { + "copy from initialized + uninitialized memory", + Bytes.fromHexString( + "0x0000000000000000000000000000000000000000000000000123456789abcdef"), + 64, + 24, + 16, + Bytes.fromHexString( + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123456789abcdef000000000000000000000000000000000000000000000000"), + false, + 2612L + }, + { + "overlapping src < dst", + Bytes.fromHexString( + "0x0123456789abcdef000000000000000000000000000000000000000000000000"), + 4, + 0, + 8, + Bytes.fromHexString( + "0x000000000123456789abcdef0000000000000000000000000000000000000000"), + false, + 2606L + }, + { + "overlapping src > dst", + Bytes.fromHexString( + "0x00112233445566778899aabbccddeeff00000000000000000000000000000000"), + 0, + 4, + 8, + Bytes.fromHexString( + "0x445566778899aabb000000000000000000000000000000000000000000000000"), + false, + 2606L + }, + { + "overlapping src == dst", + Bytes.fromHexString( + "0x00112233445566778899aabbccddeeff00000000000000000000000000000000"), + 4, + 4, + 8, + Bytes.fromHexString( + "0x00000000445566778899aabb0000000000000000000000000000000000000000"), + false, + 2606L + }, + { + "EOF-reserved pre-eof", + Bytes.fromHexString("0xEF009f918bf09f9fa9"), + 0, + 0, + 9, + Bytes.fromHexString("0xEF009f918bf09f9fa9"), + false, + 2606L + }, + { + "EOF-reserved post-epf", + Bytes.fromHexString("0xEF009f918bf09f9fa9"), + 0, + 0, + 9, + Bytes.fromHexString("0xEF000000000000000000"), + true, + 2606L + }, + { + "EF-reserved pre-epf", + Bytes.fromHexString("0xEFF09f918bf09f9fa9"), + 0, + 0, + 9, + Bytes.fromHexString("0xEFF09f918bf09f9fa9"), + false, + 2606L + }, + { + "EOF-reserved post-eof", + Bytes.fromHexString("0xEFF09f918bf09f9fa9"), + 0, + 0, + 9, + Bytes.fromHexString("0xEFF09f918bf09f9fa9"), + true, + 2606L + } + }); + } + + @SuppressWarnings("unused") + @ParameterizedTest(name = "{0}") + @MethodSource("extCodeCopyTestVector") + void testExtCodeCopy( + final String name, + final Bytes code, + final long dst, + final long src, + final long len, + final Bytes expected, + final boolean eof, + final long gasCost) { + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + account.setCode(code); + + ExtCodeCopyOperation subject = new ExtCodeCopyOperation(new PragueGasCalculator(), eof); + MessageFrame frame = + new TestMessageFrameBuilder() + .worldUpdater(worldStateUpdater) + .pushStackItem(Bytes.ofUnsignedLong(len)) + .pushStackItem(Bytes.ofUnsignedLong(src)) + .pushStackItem(Bytes.ofUnsignedLong(dst)) + .pushStackItem(REQUESTED_ADDRESS) + .build(); + + Operation.OperationResult result = subject.execute(frame, evm); + + assertThat(frame.readMemory(0, expected.size())).isEqualTo(expected); + assertThat(frame.memoryWordSize()).isEqualTo((expected.size() + 31) / 32); + assertThat(result.getGasCost()).isEqualTo(gasCost); + } + + @Test + void testExtCodeCopyCold() { + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + Bytes code = Bytes.fromHexString("0xEFF09f918bf09f9fa9"); + account.setCode(code); + + ExtCodeCopyOperation subject = new ExtCodeCopyOperation(new PragueGasCalculator(), false); + MessageFrame frame = + new TestMessageFrameBuilder() + .worldUpdater(worldStateUpdater) + .pushStackItem(Bytes.ofUnsignedLong(9)) + .pushStackItem(Bytes.ofUnsignedLong(0)) + .pushStackItem(Bytes.ofUnsignedLong(0)) + .pushStackItem(REQUESTED_ADDRESS) + .build(); + frame.warmUpAddress(REQUESTED_ADDRESS); + + Operation.OperationResult result = subject.execute(frame, evm); + + assertThat(frame.readMemory(0, 9)).isEqualTo(code); + assertThat(frame.memoryWordSize()).isEqualTo(1); + assertThat(result.getGasCost()).isEqualTo(106); + } + + @Test + void testExtCodeEOFDirtyMemory() { + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + Bytes code = Bytes.fromHexString("0xEF009f918bf09f9fa9"); + account.setCode(code); + + ExtCodeCopyOperation subject = new ExtCodeCopyOperation(new PragueGasCalculator(), true); + MessageFrame frame = + new TestMessageFrameBuilder() + .worldUpdater(worldStateUpdater) + .pushStackItem(Bytes.ofUnsignedLong(9)) + .pushStackItem(Bytes.ofUnsignedLong(0)) + .pushStackItem(Bytes.ofUnsignedLong(0)) + .pushStackItem(REQUESTED_ADDRESS) + .build(); + frame.writeMemory(0, 15, Bytes.fromHexString("0x112233445566778899aabbccddeeff")); + + Operation.OperationResult result = subject.execute(frame, evm); + + assertThat(frame.readMemory(0, 16)) + .isEqualTo(Bytes.fromHexString("0xEF0000000000000000aabbccddeeff00")); + assertThat(frame.memoryWordSize()).isEqualTo(1); + assertThat(result.getGasCost()).isEqualTo(2603); + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCodeHashOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperationTest.java similarity index 70% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCodeHashOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperationTest.java index a7ca5eefe6..97461cdbcd 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCodeHashOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; @@ -24,8 +24,8 @@ import org.hyperledger.besu.evm.frame.BlockValues; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; +import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; import org.hyperledger.besu.evm.internal.Words; -import org.hyperledger.besu.evm.operation.ExtCodeHashOperation; import org.hyperledger.besu.evm.operation.Operation.OperationResult; import org.hyperledger.besu.evm.testutils.FakeBlockValues; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; @@ -44,9 +44,11 @@ class ExtCodeHashOperationTest { private final WorldUpdater worldStateUpdater = toyWorld.updater(); private final ExtCodeHashOperation operation = - new ExtCodeHashOperation(new ConstantinopleGasCalculator()); + new ExtCodeHashOperation(new ConstantinopleGasCalculator(), false); private final ExtCodeHashOperation operationIstanbul = - new ExtCodeHashOperation(new IstanbulGasCalculator()); + new ExtCodeHashOperation(new IstanbulGasCalculator(), false); + private final ExtCodeHashOperation operationEOF = + new ExtCodeHashOperation(new PragueGasCalculator(), true); @Test void shouldCharge400Gas() { @@ -113,6 +115,50 @@ class ExtCodeHashOperationTest { assertThat(frame.getStackItem(0)).isEqualTo(Hash.hash(code)); } + @Test + void shouldGetNonEOFHash() { + final Bytes code = Bytes.fromHexString("0xEFF09f918bf09f9fa9"); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + account.setCode(code); + final UInt256 value = + UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS)) + .add(UInt256.valueOf(2).pow(UInt256.valueOf(160))); + + final MessageFrame frame = createMessageFrame(value); + operation.execute(frame, null); + assertThat(frame.getStackItem(0)).isEqualTo(Hash.hash(code)); + + final MessageFrame frameIstanbul = createMessageFrame(value); + operationIstanbul.execute(frameIstanbul, null); + assertThat(frameIstanbul.getStackItem(0)).isEqualTo(Hash.hash(code)); + + final MessageFrame frameEOF = createMessageFrame(value); + operationEOF.execute(frameEOF, null); + assertThat(frameEOF.getStackItem(0)).isEqualTo(Hash.hash(code)); + } + + @Test + void shouldGetEOFHash() { + final Bytes code = Bytes.fromHexString("0xEF009f918bf09f9fa9"); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + account.setCode(code); + final UInt256 value = + UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS)) + .add(UInt256.valueOf(2).pow(UInt256.valueOf(160))); + + final MessageFrame frame = createMessageFrame(value); + operation.execute(frame, null); + assertThat(frame.getStackItem(0)).isEqualTo(Hash.hash(code)); + + final MessageFrame frameIstanbul = createMessageFrame(value); + operationIstanbul.execute(frameIstanbul, null); + assertThat(frameIstanbul.getStackItem(0)).isEqualTo(Hash.hash(code)); + + final MessageFrame frameEOF = createMessageFrame(value); + operationEOF.execute(frameEOF, null); + assertThat(frameEOF.getStackItem(0)).isEqualTo(Hash.hash(Bytes.fromHexString("0xef00"))); + } + private Bytes executeOperation(final Address requestedAddress) { final MessageFrame frame = createMessageFrame(requestedAddress); operation.execute(frame, null); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperationTest.java new file mode 100644 index 0000000000..8c32469223 --- /dev/null +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperationTest.java @@ -0,0 +1,183 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.evm.operation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.frame.BlockValues; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; +import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; +import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; +import org.hyperledger.besu.evm.internal.Words; +import org.hyperledger.besu.evm.operation.Operation.OperationResult; +import org.hyperledger.besu.evm.testutils.FakeBlockValues; +import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; +import org.hyperledger.besu.evm.toy.ToyWorld; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt256; +import org.junit.jupiter.api.Test; + +class ExtCodeSizeOperationTest { + + private static final Address REQUESTED_ADDRESS = Address.fromHexString("0x22222222"); + + private final ToyWorld toyWorld = new ToyWorld(); + private final WorldUpdater worldStateUpdater = toyWorld.updater(); + + private final ExtCodeSizeOperation operation = + new ExtCodeSizeOperation(new ConstantinopleGasCalculator(), false); + private final ExtCodeSizeOperation operationIstanbul = + new ExtCodeSizeOperation(new IstanbulGasCalculator(), false); + private final ExtCodeSizeOperation operationEOF = + new ExtCodeSizeOperation(new PragueGasCalculator(), true); + + @Test + void shouldCharge700Gas() { + final OperationResult result = operation.execute(createMessageFrame(REQUESTED_ADDRESS), null); + assertThat(result.getGasCost()).isEqualTo(700L); + } + + @Test + void istanbulShouldCharge700Gas() { + final OperationResult result = + operationIstanbul.execute(createMessageFrame(REQUESTED_ADDRESS), null); + assertThat(result.getGasCost()).isEqualTo(700L); + } + + @Test + void shouldReturnZeroWhenAccountDoesNotExist() { + final Bytes result = executeOperation(REQUESTED_ADDRESS); + assertThat(result.trimLeadingZeros()).isEqualTo(Bytes.EMPTY); + } + + @Test + void shouldReturnSizeOfEmptyDataWhenAccountExistsButDoesNotHaveCode() { + worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).setBalance(Wei.of(1)); + assertThat(executeOperation(REQUESTED_ADDRESS).toInt()).isZero(); + } + + @Test + void shouldReturnZeroWhenAccountExistsButIsEmpty() { + worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + assertThat(executeOperation(REQUESTED_ADDRESS).trimLeadingZeros()).isEqualTo(Bytes.EMPTY); + } + + @Test + void shouldReturnZeroWhenPrecompiledContractHasNoBalance() { + assertThat(executeOperation(Address.ECREC).trimLeadingZeros()).isEqualTo(Bytes.EMPTY); + } + + @Test + void shouldReturnEmptyCodeSizeWhenPrecompileHasBalance() { + // Sending money to a precompile causes it to exist in the world state archive. + worldStateUpdater.getOrCreate(Address.ECREC).setBalance(Wei.of(10)); + assertThat(executeOperation(Address.ECREC).toInt()).isZero(); + } + + @Test + void shouldGetSizeOfAccountCodeWhenCodeIsPresent() { + final Bytes code = Bytes.fromHexString("0xabcdef"); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + account.setCode(code); + assertThat(executeOperation(REQUESTED_ADDRESS).toInt()).isEqualTo(3); + } + + @Test + void shouldZeroOutLeftMostBitsToGetAddress() { + // If EXTCODESIZE of A is X, then EXTCODESIZE of A + 2**160 is X. + final Bytes code = Bytes.fromHexString("0xabcdef"); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + account.setCode(code); + final UInt256 value = + UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS)) + .add(UInt256.valueOf(2).pow(UInt256.valueOf(160))); + final MessageFrame frame = createMessageFrame(value); + operation.execute(frame, null); + assertThat(frame.getStackItem(0).toInt()).isEqualTo(3); + } + + @Test + void shouldGetNonEOFSize() { + final Bytes code = Bytes.fromHexString("0xEFF09f918bf09f9fa9"); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + account.setCode(code); + final UInt256 value = + UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS)) + .add(UInt256.valueOf(2).pow(UInt256.valueOf(160))); + + final MessageFrame frame = createMessageFrame(value); + operation.execute(frame, null); + assertThat(frame.getStackItem(0).toInt()).isEqualTo(9); + + final MessageFrame frameIstanbul = createMessageFrame(value); + operationIstanbul.execute(frameIstanbul, null); + assertThat(frame.getStackItem(0).toInt()).isEqualTo(9); + + final MessageFrame frameEOF = createMessageFrame(value); + operationEOF.execute(frameEOF, null); + assertThat(frame.getStackItem(0).toInt()).isEqualTo(9); + } + + @Test + void shouldGetEOFSize() { + final Bytes code = Bytes.fromHexString("0xEF009f918bf09f9fa9"); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); + account.setCode(code); + final UInt256 value = + UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS)) + .add(UInt256.valueOf(2).pow(UInt256.valueOf(160))); + + final MessageFrame frame = createMessageFrame(value); + operation.execute(frame, null); + assertThat(frame.getStackItem(0).toInt()).isEqualTo(9); + + final MessageFrame frameIstanbul = createMessageFrame(value); + operationIstanbul.execute(frameIstanbul, null); + + assertThat(frameIstanbul.getStackItem(0).toInt()).isEqualTo(9); + final MessageFrame frameEOF = createMessageFrame(value); + operationEOF.execute(frameEOF, null); + assertThat(frameEOF.getStackItem(0).toInt()).isEqualTo(2); + } + + private Bytes executeOperation(final Address requestedAddress) { + final MessageFrame frame = createMessageFrame(requestedAddress); + operation.execute(frame, null); + return frame.getStackItem(0); + } + + private MessageFrame createMessageFrame(final Address requestedAddress) { + final UInt256 stackItem = Words.fromAddress(requestedAddress); + return createMessageFrame(stackItem); + } + + private MessageFrame createMessageFrame(final UInt256 stackItem) { + final BlockValues blockValues = new FakeBlockValues(1337); + final MessageFrame frame = + new TestMessageFrameBuilder() + .worldUpdater(worldStateUpdater) + .blockValues(blockValues) + .build(); + + frame.pushStackItem(stackItem); + return frame; + } +} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperationTest.java similarity index 98% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperationTest.java index d83e9f3ddc..e1881892d5 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtDelegateCallOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -29,8 +29,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.operation.AbstractExtCallOperation; -import org.hyperledger.besu.evm.operation.ExtDelegateCallOperation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.hyperledger.besu.evm.worldstate.WorldUpdater; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtStaticCallOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperationTest.java similarity index 97% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/ExtStaticCallOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperationTest.java index 76f267ec53..90f2d30088 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtStaticCallOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -29,8 +29,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.operation.AbstractExtCallOperation; -import org.hyperledger.besu.evm.operation.ExtStaticCallOperation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.hyperledger.besu.evm.worldstate.WorldUpdater; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/JumpFOperationTest.java similarity index 93% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/JumpFOperationTest.java index 52a7211050..4611faa0f8 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/JumpFOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode; @@ -23,8 +23,6 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.code.CodeSection; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.operation.JumpFOperation; -import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.apache.tuweni.bytes.Bytes; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/JumpOperationTest.java similarity index 98% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/JumpOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/JumpOperationTest.java index 3c466422ed..7b9fab338e 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/JumpOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/JumpOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; @@ -24,7 +24,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.operation.JumpOperation; import org.hyperledger.besu.evm.operation.Operation.OperationResult; import org.hyperledger.besu.evm.testutils.FakeBlockValues; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/LondonSStoreOperationGasCostTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/LondonSStoreOperationGasCostTest.java similarity index 98% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/LondonSStoreOperationGasCostTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/LondonSStoreOperationGasCostTest.java index f2cf950683..b4865df5d2 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/LondonSStoreOperationGasCostTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/LondonSStoreOperationGasCostTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/MCopyOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/MCopyOperationTest.java similarity index 96% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/MCopyOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/MCopyOperationTest.java index 2975312920..6bf5a8afd2 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/MCopyOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/MCopyOperationTest.java @@ -12,15 +12,13 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; -import org.hyperledger.besu.evm.operation.MCopyOperation; -import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import java.util.Arrays; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/PrevRanDaoOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/PrevRanDaoOperationTest.java similarity index 94% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/PrevRanDaoOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/PrevRanDaoOperationTest.java index 25a2682e87..28340920a0 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/PrevRanDaoOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/PrevRanDaoOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -23,8 +23,6 @@ import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.BlockValues; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; -import org.hyperledger.besu.evm.operation.Operation; -import org.hyperledger.besu.evm.operation.PrevRanDaoOperation; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/Push0OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/Push0OperationTest.java similarity index 94% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/Push0OperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/Push0OperationTest.java index 3309fccce8..d7440b0796 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/Push0OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/Push0OperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -24,9 +24,7 @@ import org.hyperledger.besu.evm.frame.BlockValues; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.operation.Operation.OperationResult; -import org.hyperledger.besu.evm.operation.Push0Operation; import org.hyperledger.besu.evm.testutils.FakeBlockValues; import java.util.Optional; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/RelativeJumpOperationTest.java similarity index 96% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/RelativeJumpOperationTest.java index 3104225775..7ca5eb04ca 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/RelativeJumpOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/RelativeJumpOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode; @@ -23,10 +23,6 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.Words; -import org.hyperledger.besu.evm.operation.Operation; -import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation; -import org.hyperledger.besu.evm.operation.RelativeJumpOperation; -import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.apache.tuweni.bytes.Bytes; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/RetFOperationTest.java similarity index 93% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/RetFOperationTest.java index 672628140c..f2a0947086 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/RetFOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/RetFOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode; @@ -24,8 +24,6 @@ import org.hyperledger.besu.evm.code.CodeSection; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.ReturnStack; -import org.hyperledger.besu.evm.operation.Operation; -import org.hyperledger.besu.evm.operation.RetFOperation; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.apache.tuweni.bytes.Bytes; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/RevertOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/RevertOperationTest.java similarity index 95% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/RevertOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/RevertOperationTest.java index 9ffc422d60..d5094494a3 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/RevertOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/RevertOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyLong; @@ -20,7 +20,6 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; -import org.hyperledger.besu.evm.operation.RevertOperation; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/SStoreOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/SStoreOperationTest.java similarity index 97% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/SStoreOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/SStoreOperationTest.java index 3993d9f81e..6f3c7fd21f 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/SStoreOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/SStoreOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.INSUFFICIENT_GAS; @@ -25,7 +25,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.operation.Operation.OperationResult; -import org.hyperledger.besu.evm.operation.SStoreOperation; import org.hyperledger.besu.evm.testutils.FakeBlockValues; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.hyperledger.besu.evm.toy.ToyWorld; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/SarOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/SarOperationTest.java similarity index 98% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/SarOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/SarOperationTest.java index 86182e47e3..c7d7cad00d 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/SarOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/SarOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -22,7 +22,6 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; -import org.hyperledger.besu.evm.operation.SarOperation; import java.util.List; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/SelfDestructOperationTest.java similarity index 97% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/SelfDestructOperationTest.java index 7842174e90..d3a3db754b 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/SelfDestructOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.atLeast; @@ -31,8 +31,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.Words; -import org.hyperledger.besu.evm.operation.Operation; -import org.hyperledger.besu.evm.operation.SelfDestructOperation; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.apache.tuweni.bytes.Bytes; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ShlOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ShlOperationTest.java similarity index 98% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/ShlOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/ShlOperationTest.java index b567cb6302..a4d0b8f310 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ShlOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ShlOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -22,7 +22,6 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; -import org.hyperledger.besu.evm.operation.ShlOperation; import java.util.Arrays; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ShrOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/ShrOperationTest.java similarity index 98% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/ShrOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/ShrOperationTest.java index 659c50d35c..5acc7466a2 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ShrOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/ShrOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -22,7 +22,6 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; -import org.hyperledger.besu.evm.operation.ShrOperation; import java.util.Arrays; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/TStoreOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/TStoreOperationTest.java similarity index 99% rename from evm/src/test/java/org/hyperledger/besu/evm/operations/TStoreOperationTest.java rename to evm/src/test/java/org/hyperledger/besu/evm/operation/TStoreOperationTest.java index c73a4544f9..c7c3470825 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/TStoreOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/TStoreOperationTest.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.evm.operations; +package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.INSUFFICIENT_GAS; @@ -26,8 +26,6 @@ import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.operation.Operation.OperationResult; -import org.hyperledger.besu.evm.operation.TLoadOperation; -import org.hyperledger.besu.evm.operation.TStoreOperation; import org.hyperledger.besu.evm.testutils.ByteCodeBuilder; import org.hyperledger.besu.evm.testutils.FakeBlockValues; import org.hyperledger.besu.evm.testutils.TestCodeExecutor; diff --git a/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyWorld.java b/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyWorld.java index e1e9c4c3ad..479376a8fe 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyWorld.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyWorld.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.worldstate.AuthorizedCodeService; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.Collection; @@ -32,6 +33,7 @@ public class ToyWorld implements WorldUpdater { ToyWorld parent; Map accounts = new HashMap<>(); + private AuthorizedCodeService authorizedCodeService; public ToyWorld() { this(null); @@ -39,6 +41,7 @@ public class ToyWorld implements WorldUpdater { public ToyWorld(final ToyWorld parent) { this.parent = parent; + this.authorizedCodeService = new AuthorizedCodeService(); } @Override @@ -49,11 +52,11 @@ public class ToyWorld implements WorldUpdater { @Override public Account get(final Address address) { if (accounts.containsKey(address)) { - return accounts.get(address); + return authorizedCodeService.processAccount(this, accounts.get(address), address); } else if (parent != null) { - return parent.get(address); + return authorizedCodeService.processAccount(this, parent.get(address), address); } else { - return null; + return authorizedCodeService.processAccount(this, null, address); } } @@ -70,17 +73,17 @@ public class ToyWorld implements WorldUpdater { final Bytes code) { ToyAccount account = new ToyAccount(parentAccount, address, nonce, balance, code); accounts.put(address, account); - return account; + return authorizedCodeService.processMutableAccount(this, account, address); } @Override public MutableAccount getAccount(final Address address) { if (accounts.containsKey(address)) { - return accounts.get(address); + return authorizedCodeService.processMutableAccount(this, accounts.get(address), address); } else if (parent != null) { Account parentAccount = parent.getAccount(address); if (parentAccount == null) { - return null; + return authorizedCodeService.processMutableAccount(this, null, address); } else { return createAccount( parentAccount, @@ -90,7 +93,7 @@ public class ToyWorld implements WorldUpdater { parentAccount.getCode()); } } else { - return null; + return authorizedCodeService.processMutableAccount(this, null, address); } } @@ -128,4 +131,9 @@ public class ToyWorld implements WorldUpdater { public Optional parentUpdater() { return Optional.empty(); } + + @Override + public void setAuthorizedCodeService(final AuthorizedCodeService authorizedCodeService) { + this.authorizedCodeService = authorizedCodeService; + } } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index bd437c3a4e..950e0ee866 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1385,9 +1385,9 @@ - - - + + + @@ -4632,12 +4632,12 @@ - - - + + + - - + + @@ -4648,52 +4648,52 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 0c2289233d..6d176ba7e3 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -156,7 +156,7 @@ dependencyManagement { dependency 'org.openjdk.jol:jol-core:0.17' dependency 'tech.pegasys:jc-kzg-4844:1.0.0' - dependencySet(group: 'org.hyperledger.besu', version: '0.9.2') { + dependencySet(group: 'org.hyperledger.besu', version: '0.9.3') { entry 'arithmetic' entry 'ipa-multipoint' entry 'bls12-381' diff --git a/metrics/core/src/main/java/org/hyperledger/besu/metrics/BesuMetricCategory.java b/metrics/core/src/main/java/org/hyperledger/besu/metrics/BesuMetricCategory.java index d419a6ac7c..4151320177 100644 --- a/metrics/core/src/main/java/org/hyperledger/besu/metrics/BesuMetricCategory.java +++ b/metrics/core/src/main/java/org/hyperledger/besu/metrics/BesuMetricCategory.java @@ -53,7 +53,9 @@ public enum BesuMetricCategory implements MetricCategory { /** Transaction pool besu metric category. */ TRANSACTION_POOL("transaction_pool"), /** Stratum besu metric category. */ - STRATUM("stratum"); + STRATUM("stratum"), + /** Block processing besu metric category. */ + BLOCK_PROCESSING("block_processing"); private static final Optional BESU_PREFIX = Optional.of("besu_"); diff --git a/nat/src/main/java/org/hyperledger/besu/nat/upnp/UpnpNatManager.java b/nat/src/main/java/org/hyperledger/besu/nat/upnp/UpnpNatManager.java index a7386d7f78..292fb0b007 100644 --- a/nat/src/main/java/org/hyperledger/besu/nat/upnp/UpnpNatManager.java +++ b/nat/src/main/java/org/hyperledger/besu/nat/upnp/UpnpNatManager.java @@ -101,7 +101,10 @@ public class UpnpNatManager extends AbstractNatManager { new BesuUpnpRegistryListener() { @Override public void remoteDeviceAdded(final Registry registry, final RemoteDevice device) { - LOG.debug("UPnP Device discovered: " + device.getDetails().getFriendlyName()); + LOG.atDebug() + .setMessage("UPnP Device discovered: {}") + .addArgument(device.getDetails().getFriendlyName()) + .log(); inspectDeviceRecursive(device, recognizedServices.keySet()); } }; diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 1ea78e7f3a..81e66a06a0 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -70,7 +70,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'vWxI4pduLCvzqU73k5kJsoeK9t7QTqOBC9IxksFuzBs=' + knownHash = '6L5dNJ975Ka/X7g4lTdpkBvPQrJgJu+vAf/m1dFCneU=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/BesuPlugin.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/BesuPlugin.java index 952f1fb074..1d7acf3a3e 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/BesuPlugin.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/BesuPlugin.java @@ -105,6 +105,6 @@ public interface BesuPlugin { Optional.ofNullable(pluginPackage.getImplementationVersion()) .filter(version -> !version.isBlank()) .orElse(""); - return implTitle + "/v" + implVersion; + return implTitle + "/" + implVersion; } } diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java index b59fb26967..66b1c1f2d6 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java @@ -42,6 +42,13 @@ public class TransactionSelectionResult { */ boolean discard(); + /** + * Should the score of this transaction be decremented? + * + * @return yes if the score of this transaction needs to be decremented + */ + boolean penalize(); + /** * Name of this status * @@ -52,30 +59,34 @@ public class TransactionSelectionResult { private enum BaseStatus implements Status { SELECTED, - BLOCK_FULL(true, false), - BLOBS_FULL(false, false), - BLOCK_OCCUPANCY_ABOVE_THRESHOLD(true, false), - BLOCK_SELECTION_TIMEOUT(true, false), - TX_EVALUATION_TOO_LONG(true, true), - INVALID_TRANSIENT(false, false), - INVALID(false, true); + BLOCK_FULL(true, false, false), + BLOBS_FULL(false, false, false), + BLOCK_OCCUPANCY_ABOVE_THRESHOLD(true, false, false), + BLOCK_SELECTION_TIMEOUT(true, false, false), + TX_EVALUATION_TOO_LONG(true, false, true), + INVALID_TX_EVALUATION_TOO_LONG(true, true, true), + INVALID_TRANSIENT(false, false, true), + INVALID(false, true, false); private final boolean stop; private final boolean discard; + private final boolean penalize; BaseStatus() { this.stop = false; this.discard = false; + this.penalize = false; } - BaseStatus(final boolean stop, final boolean discard) { + BaseStatus(final boolean stop, final boolean discard, final boolean penalize) { this.stop = stop; this.discard = discard; + this.penalize = penalize; } @Override public String toString() { - return name() + " (stop=" + stop + ", discard=" + discard + ")"; + return name() + " (stop=" + stop + ", discard=" + discard + ", penalize=" + penalize + ")"; } @Override @@ -87,6 +98,11 @@ public class TransactionSelectionResult { public boolean discard() { return discard; } + + @Override + public boolean penalize() { + return penalize; + } } /** The transaction has been selected to be included in the new block */ @@ -105,10 +121,14 @@ public class TransactionSelectionResult { public static final TransactionSelectionResult BLOCK_SELECTION_TIMEOUT = new TransactionSelectionResult(BaseStatus.BLOCK_SELECTION_TIMEOUT); - /** Transaction took too much to evaluate */ + /** Transaction took too much to evaluate, but it was not invalid */ public static final TransactionSelectionResult TX_EVALUATION_TOO_LONG = new TransactionSelectionResult(BaseStatus.TX_EVALUATION_TOO_LONG); + /** Transaction took too much to evaluate, and it was invalid */ + public static final TransactionSelectionResult INVALID_TX_EVALUATION_TOO_LONG = + new TransactionSelectionResult(BaseStatus.INVALID_TX_EVALUATION_TOO_LONG); + /** * The transaction has not been selected since too large and the occupancy of the block is enough * to stop the selection. @@ -215,6 +235,15 @@ public class TransactionSelectionResult { return status.discard(); } + /** + * Should the score of this transaction be decremented? + * + * @return yes if the score of this transaction needs to be decremented + */ + public boolean penalize() { + return status.penalize(); + } + /** * Is the candidate transaction selected for block inclusion? *