Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snapshot based non-persisting MutableWorldState usage #4531

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8e51417
initial plubming of Bonsai snapshots into BonsaiWorldStateArchive and…
garyschulte Oct 12, 2022
d2acef7
fix test mocks, trieLogManager map type
garyschulte Oct 13, 2022
f2080e4
implement rocksdb transaction based snapshots as well as BonsaiWorldS…
garyschulte Oct 13, 2022
4143439
add try-with-resources to blockchain queries and some rpc endpoints
garyschulte Oct 16, 2022
b491895
initial plumbing so snapshot and non-snapshot bonsai can co-exist
garyschulte Oct 16, 2022
21abcb5
fix unit test mock
garyschulte Oct 16, 2022
fbcd762
plumb snapshots into StorageProvider(s)
garyschulte Oct 18, 2022
be0839b
add CLI param --Xbonsai-use-snapshots
garyschulte Oct 18, 2022
f286122
fix bonsai worldstate tests broken in refactor
garyschulte Oct 18, 2022
f2fd8d3
add snapshots config to runner/builder tests
garyschulte Oct 18, 2022
cf55a97
fix acceptance tests
garyschulte Oct 19, 2022
23f3837
initial implementation of snapshot-based layered worldstate
garyschulte Oct 22, 2022
d2e9823
create TrieLogManager based on useSnapshots param, add TrieLogManager…
garyschulte Oct 24, 2022
dda3c1e
log and return Optional.empty on failed log rolling
garyschulte Oct 24, 2022
8486384
ensure we use cached layers for snapshots, BonsaiWorldStateArchive re…
garyschulte Oct 24, 2022
6a61d4a
add reference counting and implicit closing of snapshots
garyschulte Oct 25, 2022
e184c4b
make RocksDBSnapshotTransaction.copy() *also* copy the transaction st…
garyschulte Oct 26, 2022
400ff6d
add isDirty() to BonsaiWorldStateUpdater and rely on that in BonsaiSn…
garyschulte Oct 26, 2022
4c632bf
fix copy() in BonsaiSnapshotWorldState, and minor cleanup
garyschulte Oct 26, 2022
51f3eb5
cleanup for review, add changelog, address outstanding TODOs
garyschulte Oct 29, 2022
82749a9
refactor blockchainqueries and graphql adapters to use try-with-resou…
garyschulte Oct 29, 2022
7ac239f
fix refactor fallout, javadoc
garyschulte Oct 29, 2022
adafa3e
refactor transaction simulator to use try-with-resources
garyschulte Oct 29, 2022
b5c64ab
remove redunant worldstate mapping function, make getWorldState priva…
garyschulte Oct 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Updated jackson-databind library to version 2.13.4.2 addressing [CVE-2022-42003](https://nvd.nist.gov/vuln/detail/CVE-2022-42003)
- 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 interpretation in eth_getLogs RPC handler [#4513](https://github.com/hyperledger/besu/pull/4513)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.hyperledger.besu.cli.options.stable;

import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD;
import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_USE_SNAPSHOTS;

import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
Expand All @@ -34,6 +35,8 @@ public class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
private static final String BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD =
"--bonsai-maximum-back-layers-to-load";

private static final String BONSAI_STORAGE_FORMAT_USE_SNAPSHOTS = "--Xbonsai-use-snapshots";

// Use Bonsai DB
@Option(
names = {DATA_STORAGE_FORMAT},
Expand All @@ -50,6 +53,15 @@ public class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
arity = "1")
private final Long bonsaiMaxLayersToLoad = DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD;

@Option(
names = {BONSAI_STORAGE_FORMAT_USE_SNAPSHOTS},
paramLabel = "<BOOLEAN>",
hidden = true,
description =
"Use database snapshots for mutable worldstates with BONSAI (default: ${DEFAULT-VALUE}).",
arity = "1")
private final Boolean bonsaiUseSnapshots = DEFAULT_BONSAI_USE_SNAPSHOTS;

public static DataStorageOptions create() {
return new DataStorageOptions();
}
Expand All @@ -59,6 +71,7 @@ public DataStorageConfiguration toDomainObject() {
return ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(dataStorageFormat)
.bonsaiMaxLayersToLoad(bonsaiMaxLayersToLoad)
.useBonsaiSnapshots(bonsaiUseSnapshots)
.build();
}

Expand All @@ -68,6 +81,8 @@ public List<String> getCLIOptions() {
DATA_STORAGE_FORMAT,
dataStorageFormat.toString(),
BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD,
bonsaiMaxLayersToLoad.toString());
bonsaiMaxLayersToLoad.toString(),
BONSAI_STORAGE_FORMAT_USE_SNAPSHOTS,
bonsaiUseSnapshots.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateArchive;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.bonsai.TrieLogManager;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
Expand Down Expand Up @@ -632,12 +631,11 @@ private WorldStateArchive createWorldStateArchive(
switch (dataStorageConfiguration.getDataStorageFormat()) {
case BONSAI:
return new BonsaiWorldStateArchive(
new TrieLogManager(
blockchain,
(BonsaiWorldStateKeyValueStorage) worldStateStorage,
dataStorageConfiguration.getBonsaiMaxLayersToLoad()),
storageProvider,
blockchain);
(BonsaiWorldStateKeyValueStorage) worldStateStorage,
blockchain,
Optional.of(dataStorageConfiguration.getBonsaiMaxLayersToLoad()),
dataStorageConfiguration.useBonsaiSnapshots());

case FOREST:
default:
final WorldStatePreimageStorage preimageStorage =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,12 @@ public void setup() {
when(worldStatePreimageStorage.updater())
.thenReturn(mock(WorldStatePreimageStorage.Updater.class));
when(worldStateStorage.updater()).thenReturn(mock(WorldStateStorage.Updater.class));
BonsaiWorldStateKeyValueStorage.Updater bonsaiUpdater =
mock(BonsaiWorldStateKeyValueStorage.Updater.class);
BonsaiWorldStateKeyValueStorage.BonsaiUpdater bonsaiUpdater =
mock(BonsaiWorldStateKeyValueStorage.BonsaiUpdater.class);
when(bonsaiUpdater.getTrieLogStorageTransaction())
.thenReturn(mock(KeyValueStorageTransaction.class));
when(bonsaiUpdater.getTrieBranchStorageTransaction())
.thenReturn(mock(KeyValueStorageTransaction.class));
when(bonsaiWorldStateStorage.updater()).thenReturn(bonsaiUpdater);
besuControllerBuilder = visitWithMockConfigs(new MainnetBesuControllerBuilder());
}
Expand Down Expand Up @@ -167,6 +169,7 @@ public void shouldDisablePruningIfBonsaiIsEnabled() {
ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(DataStorageFormat.BONSAI)
.bonsaiMaxLayersToLoad(DataStorageConfiguration.DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD)
.useBonsaiSnapshots(DataStorageConfiguration.DEFAULT_BONSAI_USE_SNAPSHOTS)
.build());
besuControllerBuilder.build();

Expand All @@ -183,6 +186,7 @@ public void shouldUsePruningIfForestIsEnabled() {
ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(DataStorageFormat.FOREST)
.bonsaiMaxLayersToLoad(DataStorageConfiguration.DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD)
.useBonsaiSnapshots(DataStorageConfiguration.DEFAULT_BONSAI_USE_SNAPSHOTS)
.build());
besuControllerBuilder.build();

Expand Down
1 change: 1 addition & 0 deletions besu/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ ethstats-contact="contact@mail.n"
# Data storage
data-storage-format="BONSAI"
bonsai-maximum-back-layers-to-load=512
Xbonsai-use-snapshots=true

# feature flags
Xsecp256k1-native-enabled=false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -219,31 +218,37 @@ DataFetcher<Optional<AccountAdapter>> getAccountDataFetcher() {
final Address addr = dataFetchingEnvironment.getArgument("address");
final Long bn = dataFetchingEnvironment.getArgument("blockNumber");
if (bn != null) {
final Optional<WorldState> 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
.mapWorldState(
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<WorldState> ows = blockchainQuery.getWorldState(latestBn);
return ows.flatMap(
return blockchainQuery.mapWorldState(
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);
});
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ public Optional<AdapterBase> getMiner(final DataFetchingEnvironment environment)
}

return query
.getWorldState(blockNumber)
.map(ws -> ws.get(header.getCoinbase()))
.mapWorldState(blockNumber, ws -> ws.get(header.getCoinbase()))
.map(account -> (AdapterBase) new AccountAdapter(account))
.or(() -> Optional.of(new EmptyAccountAdapter(header.getCoinbase())));
}
Expand Down Expand Up @@ -147,13 +146,12 @@ public Optional<AccountAdapter> 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.mapWorldState(
bn,
ws -> {
final Address address = environment.getArgument("address");
return new AccountAdapter(ws.get(address));
});
}

public List<LogAdapter> getLogs(final DataFetchingEnvironment environment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ public Optional<AccountAdapter> getAccount(final DataFetchingEnvironment environ
blockNumber = bn;
}

return query
.getWorldState(blockNumber)
.map(ws -> new AccountAdapter(ws.get(logWithMetadata.getLogger())));
return query.mapWorldState(
blockNumber, ws -> new AccountAdapter(ws.get(logWithMetadata.getLogger())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,10 +64,8 @@ public Optional<AccountAdapter> getAccount(
final Address addr = dataFetchingEnvironment.getArgument("address");
final Long blockNumber = dataFetchingEnvironment.getArgument("blockNumber");
final long latestBlockNumber = blockchainQuery.latestBlock().get().getHeader().getNumber();
final Optional<WorldState> optionalWorldState =
blockchainQuery.getWorldState(latestBlockNumber);
return optionalWorldState
.flatMap(worldState -> Optional.ofNullable(worldState.get(addr)))
return blockchainQuery
.mapWorldState(latestBlockNumber, ws -> ws.get(addr))
.map(AccountAdapter::new);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -83,12 +82,11 @@ public Optional<AccountAdapter> 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.mapWorldState(
blockNumber,
mutableWorldState ->
new AccountAdapter(
mutableWorldState.get(transactionWithMetadata.getTransaction().getSender())));
}

public Optional<AccountAdapter> getTo(final DataFetchingEnvironment environment) {
Expand All @@ -98,15 +96,15 @@ public Optional<AccountAdapter> 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.mapWorldState(
blockNumber,
ws ->
transactionWithMetadata
.getTransaction()
.getTo()
.map(address -> new AccountAdapter(address, ws.get(address)))
// safe because mapWorldState returns Optional.ofNullable
.orElse(null));
}

public Optional<Wei> getValue() {
Expand Down Expand Up @@ -176,11 +174,7 @@ public Optional<AccountAdapter> getCreatedContract(final DataFetchingEnvironment
return Optional.empty();
}
final long blockNumber = bn.orElseGet(txBlockNumber::get);

final Optional<WorldState> ws = query.getWorldState(blockNumber);
if (ws.isPresent()) {
return Optional.of(new AccountAdapter(ws.get().get(addr.get())));
}
return query.mapWorldState(blockNumber, ws -> new AccountAdapter(ws.get(addr.get())));
}
}
return Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -74,34 +73,32 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
}

// TODO deal with mid-block locations

final Optional<WorldState> state =
blockchainQueries.get().getWorldState(blockHeaderOptional.get().getNumber());

if (state.isEmpty()) {
return emptyResponse(requestContext);
} else {
final List<StreamableAccount> 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()
.mapWorldState(
blockHeaderOptional.get().getNumber(),
state -> {
final List<StreamableAccount> 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<Hash> hashFromParameter(final BlockParameterOrBlockHash blockParameter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
() ->
blockchainQueries
.get()
.getWorldState(blockHeaderOptional.get().getNumber())
.map(
.mapWorldState(
blockHeaderOptional.get().getNumber(),
worldState ->
extractStorageAt(
requestContext, accountAddress, startKey, limit, worldState))
Expand Down
Loading