AbstractWorldUpdater.java
/*
* 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.worldstate;
import org.hyperledger.besu.collections.trie.BytesTrieSet;
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.internal.EvmConfiguration;
import java.util.Collection;
import java.util.Collections;
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
* WorldView}* provided in the constructor in memory.
*
* <p>Concrete implementation have to implement the {@link #commit()} method.
*
* @param <W> the type parameter
* @param <A> the type parameter
*/
public abstract class AbstractWorldUpdater<W extends WorldView, A extends Account>
implements WorldUpdater {
private final W world;
private final EvmConfiguration evmConfiguration;
/** The Updated accounts. */
protected Map<Address, UpdateTrackingAccount<A>> updatedAccounts = new ConcurrentHashMap<>();
/** The Deleted accounts. */
protected Set<Address> deletedAccounts =
Collections.synchronizedSet(new BytesTrieSet<>(Address.SIZE));
/**
* Instantiates a new Abstract world updater.
*
* @param world the world
* @param evmConfiguration the EVM Configuration parameters
*/
protected AbstractWorldUpdater(final W world, final EvmConfiguration evmConfiguration) {
this.world = world;
this.evmConfiguration = evmConfiguration;
}
/**
* Gets for mutation.
*
* @param address the address
* @return the for mutation
*/
protected abstract A getForMutation(Address address);
/**
* Track update tracking account.
*
* @param account the account
* @return the update tracking account
*/
protected UpdateTrackingAccount<A> track(final UpdateTrackingAccount<A> account) {
final Address address = account.getAddress();
updatedAccounts.put(address, account);
deletedAccounts.remove(address);
return account;
}
@Override
public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) {
final UpdateTrackingAccount<A> account = new UpdateTrackingAccount<>(address);
account.setNonce(nonce);
account.setBalance(balance);
return track(account);
}
@Override
public Account get(final Address address) {
// We may have updated it already, so check that first.
final MutableAccount existing = updatedAccounts.get(address);
if (existing != null) {
return existing;
}
if (deletedAccounts.contains(address)) {
return null;
}
return getForMutation(address);
}
@Override
public MutableAccount getAccount(final Address address) {
// We may have updated it already, so check that first.
final MutableAccount existing = updatedAccounts.get(address);
if (existing != null) {
return existing;
}
if (deletedAccounts.contains(address)) {
return null;
}
// Otherwise, get it from our wrapped view and create a new update tracker.
final A origin = getForMutation(address);
if (origin == null) {
return null;
} else {
return track(new UpdateTrackingAccount<>(origin));
}
}
@Override
public void deleteAccount(final Address address) {
deletedAccounts.add(address);
updatedAccounts.remove(address);
}
/**
* Creates an updater that buffer updates on top of this updater.
*
* @return a new updater on top of this updater. Updates made to the returned object will become
* visible on this updater when the returned updater is committed. Note however that updates
* to this updater <b>may or may not</b> be reflected to the created updater, so it is
* <b>strongly</b> advised to not update this updater until the returned one is discarded
* (either after having been committed, or because the updates it represents are meant to be
* discarded).
*/
@Override
public WorldUpdater updater() {
return switch (evmConfiguration.worldUpdaterMode()) {
case STACKED -> new StackedUpdater<>(this, evmConfiguration);
case JOURNALED -> new JournaledUpdater<>(this, evmConfiguration);
};
}
/**
* The world view on top of which this buffer updates.
*
* @return The world view on top of which this buffer updates.
*/
protected W wrappedWorldView() {
return world;
}
@Override
public Optional<WorldUpdater> parentUpdater() {
if (world instanceof WorldUpdater worldUpdater) {
return Optional.of(worldUpdater);
} else {
return Optional.empty();
}
}
/**
* The accounts modified in this updater.
*
* @return The accounts modified in this updater.
*/
protected Collection<UpdateTrackingAccount<A>> getUpdatedAccounts() {
return updatedAccounts.values();
}
/**
* The accounts deleted as part of this updater.
*
* @return The accounts deleted as part of this updater.
*/
protected Collection<Address> getDeletedAccounts() {
return deletedAccounts;
}
/** Reset. */
protected void reset() {
updatedAccounts.clear();
deletedAccounts.clear();
}
}