diff --git a/CHANGELOG.md b/CHANGELOG.md index cd6fe5213a0..be6521e960c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,14 @@ ### Additions and Improvements - Updated jackson-databind library to version 2.13.4.2 addressing [CVE-2022-42003](https://nvd.nist.gov/vuln/detail/CVE-2022-42003) -- Improve calculateRootHash method performance during Block processing [#4568](https://github.com/hyperledger/besu/pull/4568) +- Gradle task allows custom docker image configs e.g. `./gradlew distDocker -PdockerImageName=my/besu -PdockerVariants=openjdk-17,openjdk-19` +- Update snapsync feature to avoid restarting the download of the world state from scratch when restarting Besu [#4381](https://github.com/hyperledger/besu/pull/4381) +- Added worldstate snapshot isolation to improve the stability of bonsai (`--Xbonsai-use-snapshots=true`) [#4351](https://github.com/hyperledger/besu/pull/4531) ### Bug Fixes -- Fixed default fromBlock value and improved parameter interpetation in eth_getLogs RPC handler [#4513](https://github.com/hyperledger/besu/pull/4513) -- Corrects treating a block as bad on internal error during either validation or processing [#4512](https://github.com/hyperledger/besu/issues/4512) +- Fixed default fromBlock value and improved parameter interpretation in eth_getLogs RPC handler [#4513](https://github.com/hyperledger/besu/pull/4513) - Fix for NoSuchElementException for missing invalid reason when rejecting a local sent transaction [#4569](https://github.com/hyperledger/besu/pull/4569) +- Corrects treating a block as bad on internal error during either validation or processing [#4512](https://github.com/hyperledger/besu/issues/4512) ## 22.10.0-RC2 @@ -35,6 +37,7 @@ - Upgrade RocksDB database version from 6.29.5 to 7.6.0 [#4517](https://github.com/hyperledger/besu/pull/4517) - Avoid connecting to self when using static-nodes [#4521](https://github.com/hyperledger/besu/pull/4521) - EVM performance has increased 20%-100% depending on the particulars of the contract. [#4540](https://github.com/hyperledger/besu/pull/4540) +- Improve calculateRootHash method performance during Block processing [#4568](https://github.com/hyperledger/besu/pull/4568) ### Bug Fixes - Corrects emission of blockadded events when rewinding during a re-org. Fix for [#4495](https://github.com/hyperledger/besu/issues/4495) diff --git a/build.gradle b/build.gradle index 0c184613cce..353cf8b43cb 100644 --- a/build.gradle +++ b/build.gradle @@ -604,7 +604,9 @@ artifactoryPublish { dependsOn distZip } -def dockerVariants = [ +def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}" +def dockerImageName = project.hasProperty("dockerImageName") ? project.getProperty("dockerImageName") : "hyperledger/besu" +def dockerVariants = project.hasProperty("dockerVariants") ? project.getProperty("dockerVariants").split(",") : [ "openjdk-11", "openjdk-11-debug", "graalvm", @@ -634,9 +636,7 @@ tasks.register("dockerDistUntar") { task distDocker { dependsOn dockerDistUntar inputs.dir("build/docker-besu/") - def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}" def dockerBuildDir = "build/docker-besu/" - def imageName = "hyperledger/besu" doLast { for (def variant in dockerVariants) { @@ -645,7 +645,7 @@ task distDocker { into(dockerBuildDir) } exec { - def image = "${imageName}:${dockerBuildVersion}-${variant}" + def image = "${dockerImageName}:${dockerBuildVersion}-${variant}" executable "sh" workingDir dockerBuildDir args "-c", "docker build --build-arg BUILD_DATE=${buildTime()} --build-arg VERSION=${dockerBuildVersion} --build-arg VCS_REF=${getCheckedOutGitCommitHash()} -t ${image} ." @@ -654,12 +654,12 @@ task distDocker { // tag the "default" (which is the variant in the zero position) exec { executable "sh" - args "-c", "docker tag '${imageName}:${dockerBuildVersion}-${dockerVariants[0]}' '${imageName}:${dockerBuildVersion}'" + args "-c", "docker tag '${dockerImageName}:${dockerBuildVersion}-${dockerVariants[0]}' '${dockerImageName}:${dockerBuildVersion}'" } // create a static tag for the benchmark target exec { executable "sh" - args "-c", "docker tag '${imageName}:${dockerBuildVersion}-${dockerVariants[0]}' '${imageName}:benchmark'" + args "-c", "docker tag '${dockerImageName}:${dockerBuildVersion}-${dockerVariants[0]}' '${dockerImageName}:benchmark'" } } } @@ -675,7 +675,7 @@ task testDocker { doLast { for (def variant in dockerVariants) { exec { - def image = project.hasProperty('release.releaseVersion') ? "hyperledger/besu:" + project.property('release.releaseVersion') : "hyperledger/besu:${project.version}" + def image = project.hasProperty('release.releaseVersion') ? "${dockerImageName}:" + project.property('release.releaseVersion') : "${dockerImageName}:${project.version}" workingDir "${projectDir}/docker/${variant}" executable "sh" args "-c", "bash ../test.sh ${image}-${variant}" @@ -710,7 +710,7 @@ task acceptanceTestsQuorum { new File(dataDirectory).mkdirs() new File(reportsDirectory).mkdirs() - def image = project.hasProperty('release.releaseVersion') ? "hyperledger/besu:" + project.property('release.releaseVersion') : "hyperledger/besu:${project.version}" + def image = project.hasProperty('release.releaseVersion') ? "${dockerImageName}:" + project.property('release.releaseVersion') : "${dockerImageName}:${project.version}" def dockerEnv = "--env LOGGING_LEVEL_COM_QUORUM_GAUGE=DEBUG --env TF_VAR_besu_docker_image='{name=\"${image}-${variant}\",local=true}'" def dockerVolumes = "-v ${reportsDirectory}:/workspace/target/gauge/reports/ -v /var/run/docker.sock:/var/run/docker.sock -v ${dataDirectory}:${dataDirectory}" executable "sh" @@ -719,13 +719,10 @@ task acceptanceTestsQuorum { } } -def dockerImage = "hyperledger/besu" - task dockerUpload { dependsOn distDocker - def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}" def architecture = System.getenv('architecture') - def image = "${dockerImage}:${dockerBuildVersion}" + def image = "${dockerImageName}:${dockerBuildVersion}" def additionalTags = [] if (project.hasProperty('branch') && project.property('branch') == 'main') { @@ -743,7 +740,7 @@ task dockerUpload { exec { def archVariantImage = "${variantImage}-${architecture}" def cmd = "docker tag '${variantImage}' '${archVariantImage}' && docker push '${archVariantImage}'" - additionalTags.each { tag -> cmd += " && docker tag '${variantImage}' '${dockerImage}:${tag.trim()}-${variant}-${architecture}' && docker push '${dockerImage}:${tag.trim()}-${variant}-${architecture}'" } + additionalTags.each { tag -> cmd += " && docker tag '${variantImage}' '${dockerImageName}:${tag.trim()}-${variant}-${architecture}' && docker push '${dockerImageName}:${tag.trim()}-${variant}-${architecture}'" } executable "sh" args "-c", cmd } @@ -752,7 +749,7 @@ task dockerUpload { exec { def archImage = "${image}-${architecture}" def cmd = "docker tag ${image} ${archImage} && docker push '${archImage}'" - additionalTags.each { tag -> cmd += " && docker tag '${image}' '${dockerImage}:${tag.trim()}-${architecture}' && docker push '${dockerImage}:${tag.trim()}-${architecture}'" } + additionalTags.each { tag -> cmd += " && docker tag '${image}' '${dockerImageName}:${tag.trim()}-${architecture}' && docker push '${dockerImageName}:${tag.trim()}-${architecture}'" } executable "sh" args "-c", cmd } @@ -761,18 +758,17 @@ task dockerUpload { task manifestDocker { - def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}" - def image = "${dockerImage}:${dockerBuildVersion}" + def image = "${dockerImageName}:${dockerBuildVersion}" def archs = ["arm64", "amd64"] def tags = ["${image}"] if (project.hasProperty('branch') && project.property('branch') == 'main') { - tags.add("${dockerImage}:develop") + tags.add("${dockerImageName}:develop") } if (!(dockerBuildVersion ==~ /.*-SNAPSHOT/)) { - tags.add("${dockerImage}:latest") - tags.add("${dockerImage}:" + dockerBuildVersion.split(/\./)[0..1].join('.')) + tags.add("${dockerImageName}:latest") + tags.add("${dockerImageName}:" + dockerBuildVersion.split(/\./)[0..1].join('.')) } doLast { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java index 96c03910983..a5713208e70 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java @@ -46,7 +46,6 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.log.LogTopic; -import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.plugin.data.SyncStatus; import java.math.BigInteger; @@ -219,31 +218,37 @@ DataFetcher> getAccountDataFetcher() { final Address addr = dataFetchingEnvironment.getArgument("address"); final Long bn = dataFetchingEnvironment.getArgument("blockNumber"); if (bn != null) { - final Optional ws = blockchainQuery.getWorldState(bn); - if (ws.isPresent()) { - final Account account = ws.get().get(addr); - if (account == null) { - return Optional.of(new EmptyAccountAdapter(addr)); - } - return Optional.of(new AccountAdapter(account)); - } else if (bn > blockchainQuery.getBlockchain().getChainHeadBlockNumber()) { - // block is past chainhead - throw new GraphQLException(GraphQLError.INVALID_PARAMS); - } else { - // we don't have that block - throw new GraphQLException(GraphQLError.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE); - } + return blockchainQuery + .getAndMapWorldState( + bn, + ws -> { + final Account account = ws.get(addr); + if (account == null) { + return new EmptyAccountAdapter(addr); + } + return new AccountAdapter(account); + }) + .or( + () -> { + if (bn > blockchainQuery.getBlockchain().getChainHeadBlockNumber()) { + // block is past chainhead + throw new GraphQLException(GraphQLError.INVALID_PARAMS); + } else { + // we don't have that block + throw new GraphQLException(GraphQLError.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE); + } + }); } else { // return account on latest block final long latestBn = blockchainQuery.latestBlock().get().getHeader().getNumber(); - final Optional ows = blockchainQuery.getWorldState(latestBn); - return ows.flatMap( + return blockchainQuery.getAndMapWorldState( + latestBn, ws -> { final Account account = ws.get(addr); if (account == null) { - return Optional.of(new EmptyAccountAdapter(addr)); + return new EmptyAccountAdapter(addr); } - return Optional.of(new AccountAdapter(account)); + return new AccountAdapter(account); }); } }; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java index 002e446b5f5..6135a409f30 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/BlockAdapterBase.java @@ -96,8 +96,7 @@ public Optional getMiner(final DataFetchingEnvironment environment) } return query - .getWorldState(blockNumber) - .map(ws -> ws.get(header.getCoinbase())) + .getAndMapWorldState(blockNumber, ws -> ws.get(header.getCoinbase())) .map(account -> (AdapterBase) new AccountAdapter(account)) .or(() -> Optional.of(new EmptyAccountAdapter(header.getCoinbase()))); } @@ -147,13 +146,12 @@ public Optional getAccount(final DataFetchingEnvironment environ final BlockchainQueries query = getBlockchainQueries(environment); final long bn = header.getNumber(); - return query - .getWorldState(bn) - .map( - ws -> { - final Address address = environment.getArgument("address"); - return new AccountAdapter(ws.get(address)); - }); + return query.getAndMapWorldState( + bn, + ws -> { + final Address address = environment.getArgument("address"); + return new AccountAdapter(ws.get(address)); + }); } public List getLogs(final DataFetchingEnvironment environment) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java index a638044ea49..52f55ea3ff0 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/LogAdapter.java @@ -63,8 +63,7 @@ public Optional getAccount(final DataFetchingEnvironment environ blockNumber = bn; } - return query - .getWorldState(blockNumber) - .map(ws -> new AccountAdapter(ws.get(logWithMetadata.getLogger()))); + return query.getAndMapWorldState( + blockNumber, ws -> new AccountAdapter(ws.get(logWithMetadata.getLogger()))); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java index 96f67fa62f3..9a3d3d5fa83 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/PendingStateAdapter.java @@ -24,7 +24,6 @@ import org.hyperledger.besu.ethereum.transaction.CallParameter; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult; -import org.hyperledger.besu.evm.worldstate.WorldState; import java.util.List; import java.util.Map; @@ -65,10 +64,8 @@ public Optional getAccount( final Address addr = dataFetchingEnvironment.getArgument("address"); final Long blockNumber = dataFetchingEnvironment.getArgument("blockNumber"); final long latestBlockNumber = blockchainQuery.latestBlock().get().getHeader().getNumber(); - final Optional optionalWorldState = - blockchainQuery.getWorldState(latestBlockNumber); - return optionalWorldState - .flatMap(worldState -> Optional.ofNullable(worldState.get(addr))) + return blockchainQuery + .getAndMapWorldState(latestBlockNumber, ws -> ws.get(addr)) .map(AccountAdapter::new); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java index e0f7b30756d..454590539df 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java @@ -25,7 +25,6 @@ import org.hyperledger.besu.ethereum.core.LogWithMetadata; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; -import org.hyperledger.besu.evm.worldstate.WorldState; import java.util.ArrayList; import java.util.List; @@ -83,12 +82,11 @@ public Optional getFrom(final DataFetchingEnvironment environmen if (blockNumber == null) { blockNumber = transactionWithMetadata.getBlockNumber().orElseGet(query::headBlockNumber); } - return query - .getWorldState(blockNumber) - .map( - mutableWorldState -> - new AccountAdapter( - mutableWorldState.get(transactionWithMetadata.getTransaction().getSender()))); + return query.getAndMapWorldState( + blockNumber, + mutableWorldState -> + new AccountAdapter( + mutableWorldState.get(transactionWithMetadata.getTransaction().getSender()))); } public Optional getTo(final DataFetchingEnvironment environment) { @@ -98,15 +96,15 @@ public Optional getTo(final DataFetchingEnvironment environment) blockNumber = transactionWithMetadata.getBlockNumber().orElseGet(query::headBlockNumber); } - return query - .getWorldState(blockNumber) - .flatMap( - ws -> { - return transactionWithMetadata - .getTransaction() - .getTo() - .map(address -> new AccountAdapter(address, ws.get(address))); - }); + return query.getAndMapWorldState( + blockNumber, + ws -> + transactionWithMetadata + .getTransaction() + .getTo() + .map(address -> new AccountAdapter(address, ws.get(address))) + // safe because mapWorldState returns Optional.ofNullable + .orElse(null)); } public Optional getValue() { @@ -176,11 +174,7 @@ public Optional getCreatedContract(final DataFetchingEnvironment return Optional.empty(); } final long blockNumber = bn.orElseGet(txBlockNumber::get); - - final Optional ws = query.getWorldState(blockNumber); - if (ws.isPresent()) { - return Optional.of(new AccountAdapter(ws.get().get(addr.get()))); - } + return query.getAndMapWorldState(blockNumber, ws -> new AccountAdapter(ws.get(addr.get()))); } } return Optional.empty(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index 78579d3dd36..2b7e5ec62c1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -49,7 +49,6 @@ public enum RpcMethod { ENGINE_NEW_PAYLOAD("engine_newPayloadV1"), ENGINE_FORKCHOICE_UPDATED("engine_forkchoiceUpdatedV1"), ENGINE_EXCHANGE_TRANSITION_CONFIGURATION("engine_exchangeTransitionConfigurationV1"), - ENGINE_PREPARE_PAYLOAD("engine_preparePayload_debug"), GOQUORUM_ETH_GET_QUORUM_PAYLOAD("eth_getQuorumPayload"), GOQUORUM_STORE_RAW("goquorum_storeRaw"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountRange.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountRange.java index 28208459cee..777cc3f98bf 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountRange.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountRange.java @@ -24,7 +24,6 @@ import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.evm.worldstate.WorldState.StreamableAccount; import java.util.Collections; @@ -74,34 +73,32 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { } // TODO deal with mid-block locations - - final Optional state = - blockchainQueries.get().getWorldState(blockHeaderOptional.get().getNumber()); - - if (state.isEmpty()) { - return emptyResponse(requestContext); - } else { - final List accounts = - state - .get() - .streamAccounts(Bytes32.fromHexStringLenient(addressHash), maxResults + 1) - .collect(Collectors.toList()); - Bytes32 nextKey = Bytes32.ZERO; - if (accounts.size() == maxResults + 1) { - nextKey = accounts.get(maxResults).getAddressHash(); - accounts.remove(maxResults); - } - - return new JsonRpcSuccessResponse( - requestContext.getRequest().getId(), - new DebugAccountRangeAtResult( - accounts.stream() - .collect( - Collectors.toMap( - account -> account.getAddressHash().toString(), - account -> account.getAddress().orElse(Address.ZERO).toString())), - nextKey.toString())); - } + return blockchainQueries + .get() + .getAndMapWorldState( + blockHeaderOptional.get().getNumber(), + state -> { + final List accounts = + state + .streamAccounts(Bytes32.fromHexStringLenient(addressHash), maxResults + 1) + .collect(Collectors.toList()); + Bytes32 nextKey = Bytes32.ZERO; + if (accounts.size() == maxResults + 1) { + nextKey = accounts.get(maxResults).getAddressHash(); + accounts.remove(maxResults); + } + + return new JsonRpcSuccessResponse( + requestContext.getRequest().getId(), + new DebugAccountRangeAtResult( + accounts.stream() + .collect( + Collectors.toMap( + account -> account.getAddressHash().toString(), + account -> account.getAddress().orElse(Address.ZERO).toString())), + nextKey.toString())); + }) + .orElse(emptyResponse(requestContext)); } private Optional hashFromParameter(final BlockParameterOrBlockHash blockParameter) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java index 76e62ae78bc..488ea999ea5 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugStorageRangeAt.java @@ -104,8 +104,8 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { () -> blockchainQueries .get() - .getWorldState(blockHeaderOptional.get().getNumber()) - .map( + .getAndMapWorldState( + blockHeaderOptional.get().getNumber(), worldState -> extractStorageAt( requestContext, accountAddress, startKey, limit, worldState)) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProof.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProof.java index d7e037c6620..b2d50075792 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProof.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProof.java @@ -26,7 +26,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.proof.GetProofResult; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.proof.WorldStateProof; -import org.hyperledger.besu.evm.worldstate.WorldState; import java.util.Arrays; import java.util.List; @@ -58,27 +57,28 @@ protected Object resultByBlockHash( final Address address = requestContext.getRequiredParameter(0, Address.class); final List storageKeys = getStorageKeys(requestContext); - final Optional worldState = getBlockchainQueries().getWorldState(blockHash); - - if (worldState.isPresent()) { - Optional proofOptional = - getBlockchainQueries() - .getWorldStateArchive() - .getAccountProof(worldState.get().rootHash(), address, storageKeys); - return proofOptional - .map( - proof -> - (JsonRpcResponse) - new JsonRpcSuccessResponse( - requestContext.getRequest().getId(), - GetProofResult.buildGetProofResult(address, proof))) - .orElse( - new JsonRpcErrorResponse( - requestContext.getRequest().getId(), JsonRpcError.NO_ACCOUNT_FOUND)); - } - - return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), JsonRpcError.WORLD_STATE_UNAVAILABLE); + return getBlockchainQueries() + .getAndMapWorldState( + blockHash, + worldState -> { + Optional proofOptional = + getBlockchainQueries() + .getWorldStateArchive() + .getAccountProof(worldState.rootHash(), address, storageKeys); + return proofOptional + .map( + proof -> + (JsonRpcResponse) + new JsonRpcSuccessResponse( + requestContext.getRequest().getId(), + GetProofResult.buildGetProofResult(address, proof))) + .orElse( + new JsonRpcErrorResponse( + requestContext.getRequest().getId(), JsonRpcError.NO_ACCOUNT_FOUND)); + }) + .orElse( + new JsonRpcErrorResponse( + requestContext.getRequest().getId(), JsonRpcError.WORLD_STATE_UNAVAILABLE)); } @Override diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java index 4b9e47b17c5..1292b7117c2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java @@ -109,33 +109,43 @@ protected Object resultByBlockNumber( final BlockHeader blockHeader = maybeBlockHeader.get(); final List traceCallResults = new ArrayList<>(); - final WorldUpdater updater = transactionSimulator.getWorldUpdater(blockHeader); - try { - Arrays.stream(transactionsAndTraceTypeParameters) - .forEachOrdered( - param -> { - final WorldUpdater localUpdater = updater.updater(); - traceCallResults.add( - getSingleCallResult( - param.getTuple().getJsonCallParameter(), - param.getTuple().getTraceTypeParameter(), - blockHeader, - localUpdater)); - localUpdater.commit(); - }); - } catch (final TransactionInvalidException e) { - LOG.error("Invalid transaction simulator result"); - return new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR); - } catch (final EmptySimulatorResultException e) { - LOG.error( - "Empty simulator result, call params: {}, blockHeader: {} ", - JsonCallParameterUtil.validateAndGetCallParams(requestContext), - blockHeader); - return new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR); - } catch (final Exception e) { - return new JsonRpcErrorResponse(requestContext.getRequest().getId(), INTERNAL_ERROR); - } - return traceCallResults; + + return getBlockchainQueries() + .getAndMapWorldState( + blockHeader.getBlockHash(), + ws -> { + final WorldUpdater updater = + transactionSimulator.getEffectiveWorldStateUpdater(blockHeader, ws); + try { + Arrays.stream(transactionsAndTraceTypeParameters) + .forEachOrdered( + param -> { + final WorldUpdater localUpdater = updater.updater(); + traceCallResults.add( + getSingleCallResult( + param.getTuple().getJsonCallParameter(), + param.getTuple().getTraceTypeParameter(), + blockHeader, + localUpdater)); + localUpdater.commit(); + }); + } catch (final TransactionInvalidException e) { + LOG.error("Invalid transaction simulator result"); + return new JsonRpcErrorResponse( + requestContext.getRequest().getId(), INTERNAL_ERROR); + } catch (final EmptySimulatorResultException e) { + LOG.error( + "Empty simulator result, call params: {}, blockHeader: {} ", + JsonCallParameterUtil.validateAndGetCallParams(requestContext), + blockHeader); + return new JsonRpcErrorResponse( + requestContext.getRequest().getId(), INTERNAL_ERROR); + } catch (final Exception e) { + return new JsonRpcErrorResponse( + requestContext.getRequest().getId(), INTERNAL_ERROR); + } + return traceCallResults; + }); } private JsonNode getSingleCallResult( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EnginePreparePayload.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EnginePreparePayload.java deleted file mode 100644 index ad3bffafaed..00000000000 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EnginePreparePayload.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; - -import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; -import org.hyperledger.besu.ethereum.ProtocolContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePreparePayloadParameter; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EnginePreparePayloadResult; - -import java.util.Optional; - -import io.vertx.core.Vertx; - -public class EnginePreparePayload extends ExecutionEngineJsonRpcMethod { - private final MergeMiningCoordinator mergeCoordinator; - - public EnginePreparePayload( - final Vertx vertx, - final ProtocolContext protocolContext, - final EngineCallListener engineCallListener, - final MergeMiningCoordinator mergeCoordinator) { - super(vertx, protocolContext, engineCallListener); - this.mergeCoordinator = mergeCoordinator; - } - - @Override - public String getName() { - return RpcMethod.ENGINE_PREPARE_PAYLOAD.getMethodName(); - } - - @Override - public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) { - final EnginePreparePayloadParameter enginePreparePayloadParameter = - requestContext.getRequiredParameter(0, EnginePreparePayloadParameter.class); - - // TODO: respond with error if we're syncing - - return protocolContext - .getBlockchain() - .getBlockHeader(enginePreparePayloadParameter.getParentHash()) - .map( - parentHeader -> - new JsonRpcSuccessResponse( - requestContext.getRequest().getId(), - new EnginePreparePayloadResult( - mergeCoordinator.preparePayload( - parentHeader, - Optional.of(enginePreparePayloadParameter.getTimestamp()) - .filter(t -> !t.equals(0L)) - .orElse(System.currentTimeMillis()), - enginePreparePayloadParameter.getPrevRandao(), - enginePreparePayloadParameter.getFeeRecipient())))) - .orElseGet( - () -> - new JsonRpcErrorResponse( - requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS)); - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/EnginePreparePayloadParameter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/EnginePreparePayloadParameter.java deleted file mode 100644 index a4965f6ebc3..00000000000 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/EnginePreparePayloadParameter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.apache.tuweni.bytes.Bytes32; - -public class EnginePreparePayloadParameter { - private final Hash parentHash; - private final Address feeRecipient; - private final Bytes32 prevRandao; - private final Long timestamp; - - @JsonCreator - public EnginePreparePayloadParameter( - @JsonProperty("parentHash") final Hash parentHash, - @JsonProperty("feeRecipient") final Address feeRecipient, - @JsonProperty("timestamp") final UnsignedLongParameter timestamp, - @JsonProperty("prevRandao") final String prevRandao) { - this.parentHash = parentHash; - this.feeRecipient = feeRecipient; - this.timestamp = timestamp.getValue(); - this.prevRandao = Bytes32.fromHexStringLenient(prevRandao); - } - - public Hash getParentHash() { - return parentHash; - } - - public Address getFeeRecipient() { - return feeRecipient; - } - - public long getTimestamp() { - return timestamp; - } - - public Bytes32 getPrevRandao() { - return prevRandao; - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EnginePreparePayloadResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EnginePreparePayloadResult.java deleted file mode 100644 index 2983356e908..00000000000 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/EnginePreparePayloadResult.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results; - -import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; - -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - -@JsonPropertyOrder({"payloadId"}) -public class EnginePreparePayloadResult { - private final PayloadIdentifier payloadId; - - public EnginePreparePayloadResult(final PayloadIdentifier payloadId) { - this.payloadId = payloadId; - } - - @JsonGetter(value = "payloadId") - public String getNumber() { - return payloadId.toShortHexString(); - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java index 7f52e02837e..a29022aa0ab 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/ExecutionEngineJsonRpcMethods.java @@ -22,7 +22,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdated; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayload; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineNewPayload; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EnginePreparePayload; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineQosTimer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; @@ -83,9 +82,7 @@ protected Map create() { new EngineForkchoiceUpdated( consensusEngineServer, protocolContext, mergeCoordinator.get(), engineQosTimer), new EngineExchangeTransitionConfiguration( - consensusEngineServer, protocolContext, engineQosTimer), - new EnginePreparePayload( - consensusEngineServer, protocolContext, engineQosTimer, mergeCoordinator.get())); + consensusEngineServer, protocolContext, engineQosTimer)); } else { return mapOf( new EngineExchangeTransitionConfiguration( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java index 9af45c2ada0..0c79a7ccd2a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java @@ -29,13 +29,13 @@ import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.LogWithMetadata; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.log.LogsBloomFilter; -import org.hyperledger.besu.evm.worldstate.WorldState; import java.io.EOFException; import java.io.IOException; @@ -294,18 +294,9 @@ public long getTransactionCount(final Address address, final long blockNumber) { * @return The number of transactions sent from the given address. */ public long getTransactionCount(final Address address, final Hash blockHash) { - try (final var worldState = - getWorldState(blockHash) - .orElseThrow( - () -> - new IllegalArgumentException( - "Missing worldstate for stateroot " + blockHash.toShortHexString()))) { - return Optional.ofNullable(worldState.get(address)).map(Account::getNonce).orElse(0L); - } catch (final Exception ex) { - RuntimeException unchecked = - (ex instanceof RuntimeException) ? (RuntimeException) ex : new RuntimeException(ex); - throw unchecked; - } + return getAndMapWorldState(blockHash, worldState -> worldState.get(address)) + .map(Account::getNonce) + .orElse(0L); } /** @@ -843,29 +834,48 @@ public List matchingLogs( } /** - * Returns the world state for the corresponding block number + * Wraps an operation on MutableWorldState with try-with-resources the corresponding block hash. + * This method provides access to the worldstate via a mapper function in order to ensure all of + * the uses of the MutableWorldState are subsequently closed, via the try-with-resources block. * - * @param blockNumber the block number + * @param return type of the operation on the MutableWorldState + * @param blockHash the block hash + * @param mapper Function which performs an operation on a MutableWorldState * @return the world state at the block number */ - public Optional getWorldState(final long blockNumber) { - final Hash blockHash = - getBlockHeaderByNumber(blockNumber).map(BlockHeader::getHash).orElse(Hash.EMPTY); - // TODO: try-with-resources on callers. - return getWorldState(blockHash); + public Optional getAndMapWorldState( + final Hash blockHash, final Function mapper) { + return blockchain + .getBlockHeader(blockHash) + .flatMap( + blockHeader -> { + try (var ws = + worldStateArchive + .getMutable(blockHeader.getStateRoot(), blockHeader.getHash(), false) + .orElse(null)) { + if (ws != null) { + return Optional.ofNullable(mapper.apply(ws)); + } + } catch (Exception ex) { + LOG.error("failed worldstate query for " + blockHash.toShortHexString(), ex); + } + return Optional.empty(); + }); } /** - * Returns the world state for the corresponding block hash + * Wraps an operation on MutableWorldState with try-with-resources the corresponding block number * - * @param blockHash the block hash - * @return the world state at the block hash + * @param return type of the operation on the MutableWorldState + * @param blockNumber the block number + * @param mapper Function which performs an operation on a MutableWorldState returning type U + * @return the world state at the block number */ - public Optional getWorldState(final Hash blockHash) { - final Optional header = blockchain.getBlockHeader(blockHash); - return header.flatMap( - blockHeader -> - worldStateArchive.getMutable(blockHeader.getStateRoot(), blockHeader.getHash(), false)); + public Optional getAndMapWorldState( + final long blockNumber, final Function mapper) { + final Hash blockHash = + getBlockHeaderByNumber(blockNumber).map(BlockHeader::getHash).orElse(Hash.EMPTY); + return getAndMapWorldState(blockHash, mapper); } public Optional gasPrice() { @@ -925,26 +935,12 @@ public Optional gasPriorityFee() { (int) ((gasCollection.length) * apiConfig.getGasPriceFraction()))])); } - private Optional fromWorldState( - final Hash blockHash, final Function getter) { - try (var ws = - getWorldState(blockHash) - .orElseThrow( - () -> - new IllegalArgumentException( - "Missing worldstate for stateroot " + blockHash.toShortHexString()))) { - return Optional.of(ws).map(getter); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - private Optional fromAccount( final Address address, final Hash blockHash, final Function getter, final T noAccountValue) { - return fromWorldState( + return getAndMapWorldState( blockHash, worldState -> Optional.ofNullable(worldState.get(address)).map(getter).orElse(noAccountValue)); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/BlockDataFetcherTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/BlockDataFetcherTest.java index da5693cdd06..fbce733017d 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/BlockDataFetcherTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/BlockDataFetcherTest.java @@ -16,7 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.when; import org.hyperledger.besu.datatypes.Address; @@ -73,7 +72,6 @@ public void ibftMiner() throws Exception { when(query.blockByNumber(ArgumentMatchers.anyLong())) .thenReturn(Optional.of(new BlockWithMetadata<>(header, null, null, null, 0))); when(header.getCoinbase()).thenReturn(testAddress); - when(query.getWorldState(anyLong())).thenReturn(Optional.of(mutableWorldState)); final Optional maybeBlock = fetcher.get(environment); assertThat(maybeBlock).isPresent(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java index b8ab222f875..a207a24f96d 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java @@ -19,6 +19,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -30,6 +31,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.proof.GetProofResult; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; @@ -42,6 +44,7 @@ import java.util.Collections; import java.util.Optional; +import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -139,7 +142,7 @@ void errorWhenAccountNotFound() { void errorWhenWorldStateUnavailable() { when(blockchainQueries.headBlockNumber()).thenReturn(14L); - when(blockchainQueries.getWorldState(any())).thenReturn(Optional.empty()); + when(blockchainQueries.getAndMapWorldState(any(), any())).thenReturn(Optional.empty()); final JsonRpcErrorResponse expectedResponse = new JsonRpcErrorResponse(null, JsonRpcError.WORLD_STATE_UNAVAILABLE); @@ -221,7 +224,14 @@ private GetProofResult generateWorldState() { final MutableWorldState mutableWorldState = mock(MutableWorldState.class); when(mutableWorldState.rootHash()).thenReturn(rootHash); - when(blockchainQueries.getWorldState(any())).thenReturn(Optional.of(mutableWorldState)); + doAnswer( + invocation -> + Optional.of( + invocation + .>getArgument(1) + .apply(mutableWorldState))) + .when(blockchainQueries) + .getAndMapWorldState(any(), any()); return GetProofResult.buildGetProofResult(address, worldStateProof); } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java index af860f1f5e0..0c17537e706 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java @@ -209,7 +209,6 @@ protected BlockCreationResult createBlock( } catch (final StorageException ex) { throw ex; } catch (final Exception ex) { - // TODO(tmm): How are we going to know this has exploded, and thus restart it? throw new IllegalStateException( "Block creation failed unexpectedly. Will restart on next block added to chain.", ex); } @@ -261,7 +260,6 @@ private MutableWorldState duplicateWorldStateAtParent() { + " with state root " + parentStateRoot); }) - // TODO: only copy if this is from layered worldstate .copy(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/AbstractTrieLogManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/AbstractTrieLogManager.java index e5e220b8737..472f713da70 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/AbstractTrieLogManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/AbstractTrieLogManager.java @@ -18,7 +18,7 @@ import static org.hyperledger.besu.util.Slf4jLambdaHelper.debugLambda; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.bonsai.TrieLogManager.CachedLayer; +import org.hyperledger.besu.ethereum.bonsai.TrieLogManager.CachedWorldState; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.MutableWorldState; @@ -32,7 +32,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public abstract class AbstractTrieLogManager implements TrieLogManager { +public abstract class AbstractTrieLogManager implements TrieLogManager { private static final Logger LOG = LoggerFactory.getLogger(AbstractTrieLogManager.class); public static final long RETAINED_LAYERS = 512; // at least 256 + typical rollbacks diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiInMemoryWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiInMemoryWorldState.java index 683370c11f4..8d04717ff4e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiInMemoryWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiInMemoryWorldState.java @@ -25,7 +25,6 @@ import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; public class BonsaiInMemoryWorldState extends BonsaiPersistedWorldState { @@ -118,8 +117,8 @@ private void updateAccountStorage( for (final Map.Entry> storageUpdate : storageAccountUpdate.getValue().entrySet()) { final Hash keyHash = storageUpdate.getKey(); - final Bytes32 updatedStorage = storageUpdate.getValue().getUpdated(); - if (updatedStorage == null || updatedStorage.equals(Bytes32.ZERO)) { + final UInt256 updatedStorage = storageUpdate.getValue().getUpdated(); + if (updatedStorage == null || updatedStorage.equals(UInt256.ZERO)) { storageTrie.remove(keyHash); } else { storageTrie.put(keyHash, BonsaiWorldView.encodeTrieValue(updatedStorage)); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiPersistedWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiPersistedWorldState.java index 3ede49e5973..d2383c8b461 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiPersistedWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiPersistedWorldState.java @@ -196,8 +196,8 @@ private void updateAccountStorageState( for (final Map.Entry> storageUpdate : storageAccountUpdate.getValue().entrySet()) { final Hash keyHash = storageUpdate.getKey(); - final Bytes32 updatedStorage = storageUpdate.getValue().getUpdated(); - if (updatedStorage == null || updatedStorage.equals(Bytes32.ZERO)) { + final UInt256 updatedStorage = storageUpdate.getValue().getUpdated(); + if (updatedStorage == null || updatedStorage.equals(UInt256.ZERO)) { stateUpdater.removeStorageValueBySlotHash(updatedAddressHash, keyHash); storageTrie.remove(keyHash); } else { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotPersistedWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotPersistedWorldState.java deleted file mode 100644 index 911a9f21d55..00000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotPersistedWorldState.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Hyperledger Besu Contributors. - * - * 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.bonsai; - -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.core.MutableWorldState; -import org.hyperledger.besu.ethereum.core.SnapshotMutableWorldState; -import org.hyperledger.besu.plugin.services.storage.SnappableKeyValueStorage; -import org.hyperledger.besu.plugin.services.storage.SnappedKeyValueStorage; - -/** - * This class is similar to BonsaiSnapshotWorldState however it extends BonsaiPersistedWorldstate - * directly such that it commits/perists directly to the transaction state. The primary difference - * here is that we use RocksDBSnapshotTransaction (off-heap mem) to accumulate changes rather than - * BonsaiWorldStateUpdater (on-heap mem) - */ -public class BonsaiSnapshotPersistedWorldState extends BonsaiPersistedWorldState - implements SnapshotMutableWorldState { - - private final SnappedKeyValueStorage accountSnap; - private final SnappedKeyValueStorage codeSnap; - private final SnappedKeyValueStorage storageSnap; - private final SnappedKeyValueStorage trieBranchSnap; - - private BonsaiSnapshotPersistedWorldState( - final BonsaiWorldStateArchive archive, - final BonsaiSnapshotWorldStateKeyValueStorage snapshotWorldStateStorage) { - super(archive, snapshotWorldStateStorage); - this.accountSnap = (SnappedKeyValueStorage) snapshotWorldStateStorage.accountStorage; - this.codeSnap = (SnappedKeyValueStorage) snapshotWorldStateStorage.codeStorage; - this.storageSnap = (SnappedKeyValueStorage) snapshotWorldStateStorage.storageStorage; - this.trieBranchSnap = (SnappedKeyValueStorage) snapshotWorldStateStorage.trieBranchStorage; - } - - public static BonsaiSnapshotPersistedWorldState create( - final BonsaiWorldStateArchive archive, - final BonsaiWorldStateKeyValueStorage parentWorldStateStorage) { - return new BonsaiSnapshotPersistedWorldState( - archive, - new BonsaiSnapshotWorldStateKeyValueStorage( - ((SnappableKeyValueStorage) parentWorldStateStorage.accountStorage).takeSnapshot(), - ((SnappableKeyValueStorage) parentWorldStateStorage.codeStorage).takeSnapshot(), - ((SnappableKeyValueStorage) parentWorldStateStorage.storageStorage).takeSnapshot(), - ((SnappableKeyValueStorage) parentWorldStateStorage.trieBranchStorage).takeSnapshot(), - parentWorldStateStorage.trieLogStorage)); - } - - @Override - public Hash rootHash() { - if (updater.isDirty()) { - this.worldStateRootHash = calculateRootHash(worldStateStorage.updater(), updater); - } - return this.worldStateRootHash; - } - - @Override - public MutableWorldState copy() { - // return a clone-based copy of worldstate storage - return new BonsaiSnapshotPersistedWorldState( - archive, - new BonsaiSnapshotWorldStateKeyValueStorage( - accountSnap.cloneFromSnapshot(), - codeSnap.cloneFromSnapshot(), - storageSnap.cloneFromSnapshot(), - trieBranchSnap.cloneFromSnapshot(), - worldStateStorage.trieLogStorage)); - } - - @Override - public void close() throws Exception { - accountSnap.close(); - codeSnap.close(); - storageSnap.close(); - trieBranchSnap.close(); - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldState.java index 274235efd9f..f91a922ddd8 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldState.java @@ -15,19 +15,18 @@ */ package org.hyperledger.besu.ethereum.bonsai; -import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.SnapshotMutableWorldState; import org.hyperledger.besu.plugin.services.storage.SnappableKeyValueStorage; import org.hyperledger.besu.plugin.services.storage.SnappedKeyValueStorage; /** - * This class takes a snapshot of the worldstate as the basis of a mutable worldstate. It is able to - * commit/perist as a trielog layer only. This is useful for async blockchain operations like block - * creation and/or point-in-time queries since the snapshot worldstate is fully isolated from the - * main BonsaiPersistedWorldState. + * This class extends BonsaiPersistedWorldstate directly such that it commits/perists directly to + * the transaction state. A SnapshotMutableWorldState is used to accumulate changes to a + * non-persisting mutable world state rather than writing worldstate changes directly. */ -public class BonsaiSnapshotWorldState extends BonsaiInMemoryWorldState +public class BonsaiSnapshotWorldState extends BonsaiPersistedWorldState implements SnapshotMutableWorldState { private final SnappedKeyValueStorage accountSnap; @@ -59,30 +58,24 @@ public static BonsaiSnapshotWorldState create( } @Override - public void persist(final BlockHeader blockHeader) { - super.persist(blockHeader); - // persist roothash to trie branch snapshot tx - trieBranchSnap - .getSnapshotTransaction() - .put( - BonsaiWorldStateKeyValueStorage.WORLD_ROOT_HASH_KEY, - worldStateRootHash.toArrayUnsafe()); + public Hash rootHash() { + if (updater.isDirty()) { + this.worldStateRootHash = calculateRootHash(worldStateStorage.updater(), updater); + } + return this.worldStateRootHash; } @Override public MutableWorldState copy() { // return a clone-based copy of worldstate storage - var copy = - new BonsaiSnapshotWorldState( - archive, - new BonsaiSnapshotWorldStateKeyValueStorage( - accountSnap.cloneFromSnapshot(), - codeSnap.cloneFromSnapshot(), - storageSnap.cloneFromSnapshot(), - trieBranchSnap.cloneFromSnapshot(), - worldStateStorage.trieLogStorage)); - copy.updater.cloneFromUpdater(updater); - return copy; + return new BonsaiSnapshotWorldState( + archive, + new BonsaiSnapshotWorldStateKeyValueStorage( + accountSnap.cloneFromSnapshot(), + codeSnap.cloneFromSnapshot(), + storageSnap.cloneFromSnapshot(), + trieBranchSnap.cloneFromSnapshot(), + worldStateStorage.trieLogStorage)); } @Override diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchive.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchive.java index 71e91e947f7..937fc6795b5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchive.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchive.java @@ -137,11 +137,8 @@ public boolean isWorldStateAvailable(final Hash rootHash, final Hash blockHash) } public Optional getMutableSnapshot(final Hash blockHash) { - // TODO: decide whether we want to use BonsaiSnapshotWorldState or - // BonsaiSnapshotPersistedWorldState return rollMutableStateToBlockHash( - // BonsaiSnapshotWorldState.create(this, worldStateStorage), blockHash) - BonsaiSnapshotPersistedWorldState.create(this, worldStateStorage), blockHash) + BonsaiSnapshotWorldState.create(this, worldStateStorage), blockHash) .map(SnapshotMutableWorldState.class::cast); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java index b76b9ff8216..e6d00b8063d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java @@ -29,6 +29,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -38,6 +39,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; @@ -76,6 +78,22 @@ void cloneFromUpdater(final BonsaiWorldStateUpdater source) { deletedAccounts.addAll(source.deletedAccounts); } + @Override + public Account get(final Address address) { + return super.get(address); + } + + @Override + protected UpdateTrackingAccount track( + final UpdateTrackingAccount account) { + return super.track(account); + } + + @Override + public EvmAccount getAccount(final Address address) { + return super.getAccount(address); + } + @Override public EvmAccount createAccount(final Address address, final long nonce, final Wei balance) { BonsaiValue bonsaiValue = accountsToUpdate.get(address); @@ -242,7 +260,9 @@ public void commit() { } final TreeSet> entries = - new TreeSet<>(Map.Entry.comparingByKey()); + new TreeSet<>( + Comparator.comparing( + (Function, UInt256>) Map.Entry::getKey)); entries.addAll(updatedAccount.getUpdatedStorage().entrySet()); for (final Map.Entry storageUpdate : entries) { @@ -605,7 +625,7 @@ private void rollStorageChange( address, slotHash)); } } else { - final Bytes32 existingSlotValue = slotValue.getUpdated(); + final UInt256 existingSlotValue = slotValue.getUpdated(); if ((expectedValue == null || expectedValue.isZero()) && existingSlotValue != null && !existingSlotValue.isZero()) { @@ -636,10 +656,10 @@ private void rollStorageChange( } } - private boolean isSlotEquals(final Bytes32 expectedValue, final Bytes32 existingSlotValue) { - final Bytes32 sanitizedExpectedValue = (expectedValue == null) ? Bytes32.ZERO : expectedValue; - final Bytes32 sanitizedExistingSlotValue = - (existingSlotValue == null) ? Bytes32.ZERO : existingSlotValue; + private boolean isSlotEquals(final UInt256 expectedValue, final UInt256 existingSlotValue) { + final UInt256 sanitizedExpectedValue = (expectedValue == null) ? UInt256.ZERO : expectedValue; + final UInt256 sanitizedExistingSlotValue = + (existingSlotValue == null) ? UInt256.ZERO : existingSlotValue; return Objects.equals(sanitizedExpectedValue, sanitizedExistingSlotValue); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/LayeredTrieLogManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/LayeredTrieLogManager.java index 2d69af64f13..83c72516d44 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/LayeredTrieLogManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/LayeredTrieLogManager.java @@ -91,7 +91,7 @@ public synchronized void updateCachedLayers(final Hash blockParentHash, final Ha }); } - public static class LayeredWorldStateCache implements CachedLayer { + public static class LayeredWorldStateCache implements CachedWorldState { final BonsaiLayeredWorldState layeredWorldState; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/SnapshotTrieLogManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/SnapshotTrieLogManager.java index 6b70c4e3d6b..6ca8efe9459 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/SnapshotTrieLogManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/SnapshotTrieLogManager.java @@ -84,7 +84,7 @@ public void updateCachedLayers(final Hash blockParentHash, final Hash blockHash) // no-op. Snapshots are independent and do not need to update 'next' worldstates } - public static class CachedSnapshotWorldState implements CachedLayer { + public static class CachedSnapshotWorldState implements CachedWorldState { final BonsaiSnapshotWorldState snapshot; final TrieLogLayer trieLog; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/TrieLogManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/TrieLogManager.java index 4b9550ec808..b107513a9c0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/TrieLogManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/TrieLogManager.java @@ -43,7 +43,7 @@ void addCachedLayer( Optional getTrieLogLayer(final Hash blockHash); - interface CachedLayer { + interface CachedWorldState { long getHeight(); TrieLogLayer getTrieLog(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/GoQuorumKeyValueStorageProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/GoQuorumKeyValueStorageProvider.java index 0f8012f9c7f..5339ecb300e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/GoQuorumKeyValueStorageProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/GoQuorumKeyValueStorageProvider.java @@ -41,7 +41,7 @@ public GoQuorumKeyValueStorageProvider( worldStatePreimageStorage, privateWorldStatePreimageStorage, segmentIsolationSupported, - false); + SNAPSHOT_ISOLATION_UNSUPPORTED); } @Override diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java index 94a7e0b3251..0d86533091c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStorageProvider.java @@ -35,6 +35,9 @@ public class KeyValueStorageProvider implements StorageProvider { + public static final boolean SEGMENT_ISOLATION_SUPPORTED = true; + public static final boolean SNAPSHOT_ISOLATION_UNSUPPORTED = false; + protected final Function storageCreator; private final KeyValueStorage worldStatePreimageStorage; private final KeyValueStorage privateWorldStatePreimageStorage; @@ -50,7 +53,7 @@ public KeyValueStorageProvider( this.worldStatePreimageStorage = worldStatePreimageStorage; this.privateWorldStatePreimageStorage = null; this.isWorldStateIterable = segmentIsolationSupported; - this.isWorldStateSnappable = false; + this.isWorldStateSnappable = SNAPSHOT_ISOLATION_UNSUPPORTED; } public KeyValueStorageProvider( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index 1a2cae9002a..3ac19c03d55 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -144,21 +144,31 @@ public Optional process( return Optional.empty(); } - WorldUpdater updater; - try { - updater = getWorldUpdater(header); - } catch (final IllegalArgumentException e) { - return Optional.empty(); - } + try (var ws = getWorldState(header)) { - // in order to trace the state diff we need to make sure that - // the world updater always has a parent - if (operationTracer instanceof DebugOperationTracer) { - updater = updater.parentUpdater().isPresent() ? updater : updater.updater(); + WorldUpdater updater = getEffectiveWorldStateUpdater(header, ws); + + // in order to trace the state diff we need to make sure that + // the world updater always has a parent + if (operationTracer instanceof DebugOperationTracer) { + updater = updater.parentUpdater().isPresent() ? updater : updater.updater(); + } + + return processWithWorldUpdater( + callParams, transactionValidationParams, operationTracer, header, updater); + + } catch (final Exception e) { + return Optional.empty(); } + } - return processWithWorldUpdater( - callParams, transactionValidationParams, operationTracer, header, updater); + private MutableWorldState getWorldState(final BlockHeader header) { + return worldStateArchive + .getMutable(header.getStateRoot(), header.getHash(), false) + .orElseThrow( + () -> + new IllegalArgumentException( + "Public world state not available for block " + header.toLogString())); } @Nonnull @@ -303,20 +313,8 @@ private Optional buildTransaction( return Optional.ofNullable(transaction); } - public WorldUpdater getWorldUpdater(final BlockHeader header) { - final MutableWorldState publicWorldState = - worldStateArchive.getMutable(header.getStateRoot(), header.getHash(), false).orElse(null); - - if (publicWorldState == null) { - throw new IllegalArgumentException( - "Public world state not available for block " + header.getNumber()); - } - - return getEffectiveWorldStateUpdater(header, publicWorldState); - } - // return combined private/public world state updater if GoQuorum mode, otherwise the public state - private WorldUpdater getEffectiveWorldStateUpdater( + public WorldUpdater getEffectiveWorldStateUpdater( final BlockHeader header, final MutableWorldState publicWorldState) { if (maybePrivacyParameters.isPresent() diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java index 7976249b7fb..bccc21da8a3 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryKeyValueStorageProvider.java @@ -38,8 +38,8 @@ public InMemoryKeyValueStorageProvider() { segmentIdentifier -> new InMemoryKeyValueStorage(), new InMemoryKeyValueStorage(), new InMemoryKeyValueStorage(), - true, - false); + SEGMENT_ISOLATION_SUPPORTED, + SNAPSHOT_ISOLATION_UNSUPPORTED); } public static MutableBlockchain createInMemoryBlockchain(final Block genesisBlock) { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java index 075e12b6aee..0c066f322b0 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java @@ -40,7 +40,6 @@ import java.util.Optional; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; public class MainnetBlockValidatorTest { @@ -173,7 +172,6 @@ public void shouldDetectAndCacheInvalidBlocksWhenBodyInvalid() { } @Test - @Ignore("TODO: refactor this for mutable snapshot worldstate") public void shouldNotCacheWhenValidBlocks() { when(blockchain.getBlockHeader(any(Hash.class))) .thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader())); @@ -185,6 +183,8 @@ public void shouldNotCacheWhenValidBlocks() { .thenReturn(true); when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class), anyBoolean())) .thenReturn(Optional.of(mock(MutableWorldState.class))); + when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class))) + .thenReturn(Optional.of(mock(MutableWorldState.class))); when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(badBlock))) .thenReturn(new BlockProcessingResult(Optional.empty())); when(blockBodyValidator.validateBody( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldStateArchiveTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldStateArchiveTest.java index 8ccbbfdf0f5..2a2c16fe273 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldStateArchiveTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotWorldStateArchiveTest.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright Hyperledger Besu Contributors. * * 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 @@ -90,6 +90,4 @@ public void testGetMutableReturnEmptyWhenLoadMoreThanLimitLayersBack() { when(blockchain.getChainHeadHeader()).thenReturn(chainHead); assertThat(bonsaiWorldStateArchive.getMutable(null, blockHeader.getHash(), false)).isEmpty(); } - - // TODO: writeme } 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 2d31ef0aee8..57697e7d0a2 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 @@ -204,7 +204,9 @@ private boolean buildContext( final EthContext ethContext = new EthContext(ethPeers, new EthMessages(), ethScheduler); final TransactionPoolConfiguration transactionPoolConfiguration = - ImmutableTransactionPoolConfiguration.builder().build(); + ImmutableTransactionPoolConfiguration.builder() + .txPoolLimitByAccountPercentage(0.004f) + .build(); transactionPool = TransactionPoolFactory.createTransactionPool( 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 ea2ca2b9afa..a8f2f708a4e 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 @@ -21,11 +21,12 @@ import org.hyperledger.besu.evm.account.MutableAccount; import java.util.Collection; -import java.util.HashMap; +import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * An abstract implementation of a {@link WorldUpdater} that buffers update over the {@link @@ -38,8 +39,8 @@ public abstract class AbstractWorldUpdater> updatedAccounts = new HashMap<>(); - protected Set
deletedAccounts = new HashSet<>(); + protected Map> updatedAccounts = new ConcurrentHashMap<>(); + protected Set
deletedAccounts = Collections.synchronizedSet(new HashSet<>()); protected AbstractWorldUpdater(final W world) { this.world = world;