Skip to content

Commit

Permalink
create a bonsai based reference test worldstate
Browse files Browse the repository at this point in the history
Signed-off-by: garyschulte <garyschulte@gmail.com>
  • Loading branch information
garyschulte committed Jul 7, 2023
1 parent 6ac03af commit 37b2b07
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.hyperledger.besu.ethereum.bonsai.BonsaiAccount;
import org.hyperledger.besu.ethereum.bonsai.BonsaiValue;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateProvider;
import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoader;
import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiSnapshotWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage.BonsaiStorageSubscriber;
Expand Down Expand Up @@ -64,7 +65,8 @@ public class BonsaiWorldState

public BonsaiWorldStateKeyValueStorage worldStateStorage;

private final BonsaiWorldStateProvider archive;
private final CachedMerkleTrieLoader cachedMerkleTrieLoader;
private final TrieLogManager trieLogManager;
private final BonsaiWorldStateUpdateAccumulator accumulator;

public Hash worldStateRootHash;
Expand All @@ -75,42 +77,29 @@ public class BonsaiWorldState
public BonsaiWorldState(
final BonsaiWorldStateProvider archive,
final BonsaiWorldStateKeyValueStorage worldStateStorage) {
this.archive = archive;
this.worldStateStorage = worldStateStorage;
worldStateRootHash =
Hash.wrap(
Bytes32.wrap(worldStateStorage.getWorldStateRootHash().orElse(Hash.EMPTY_TRIE_HASH)));
worldStateBlockHash =
Hash.wrap(Bytes32.wrap(worldStateStorage.getWorldStateBlockHash().orElse(Hash.ZERO)));
accumulator =
new BonsaiWorldStateUpdateAccumulator(
this,
(addr, value) ->
archive
.getCachedMerkleTrieLoader()
.preLoadAccount(getWorldStateStorage(), worldStateRootHash, addr),
(addr, value) ->
archive
.getCachedMerkleTrieLoader()
.preLoadStorageSlot(getWorldStateStorage(), addr, value));
this(worldStateStorage, archive.getCachedMerkleTrieLoader(), archive.getTrieLogManager());
}

public BonsaiWorldState(
final BonsaiWorldStateProvider archive,
protected BonsaiWorldState(
final BonsaiWorldStateKeyValueStorage worldStateStorage,
final BonsaiWorldStateUpdateAccumulator updater) {
this.archive = archive;
final CachedMerkleTrieLoader cachedMerkleTrieLoader,
final TrieLogManager trieLogManager) {
this.worldStateStorage = worldStateStorage;
this.worldStateRootHash =
Hash.wrap(
Bytes32.wrap(worldStateStorage.getWorldStateRootHash().orElse(Hash.EMPTY_TRIE_HASH)));
this.worldStateBlockHash =
Hash.wrap(Bytes32.wrap(worldStateStorage.getWorldStateBlockHash().orElse(Hash.ZERO)));
this.accumulator = updater;
}

public BonsaiWorldStateProvider getArchive() {
return archive;
accumulator =
new BonsaiWorldStateUpdateAccumulator(
this,
(addr, value) ->
cachedMerkleTrieLoader.preLoadAccount(
getWorldStateStorage(), worldStateRootHash, addr),
(addr, value) ->
cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value));
this.cachedMerkleTrieLoader = cachedMerkleTrieLoader;
this.trieLogManager = trieLogManager;
}

@Override
Expand Down Expand Up @@ -168,9 +157,7 @@ private Hash calculateRootHash(
final StoredMerklePatriciaTrie<Bytes, Bytes> accountTrie =
createTrie(
(location, hash) ->
archive
.getCachedMerkleTrieLoader()
.getAccountStateTrieNode(worldStateStorage, location, hash),
cachedMerkleTrieLoader.getAccountStateTrieNode(worldStateStorage, location, hash),
worldStateRootHash);

// for manicured tries and composting, collect branches here (not implemented)
Expand Down Expand Up @@ -253,10 +240,8 @@ private void updateAccountStorageState(
final StoredMerklePatriciaTrie<Bytes, Bytes> storageTrie =
createTrie(
(location, key) ->
archive
.getCachedMerkleTrieLoader()
.getAccountStorageTrieNode(
worldStateStorage, updatedAddressHash, location, key),
cachedMerkleTrieLoader.getAccountStorageTrieNode(
worldStateStorage, updatedAddressHash, location, key),
storageRoot);

// for manicured tries and composting, collect branches here (not implemented)
Expand Down Expand Up @@ -382,7 +367,6 @@ public void persist(final BlockHeader blockHeader) {
}
saveTrieLog =
() -> {
final TrieLogManager trieLogManager = archive.getTrieLogManager();
trieLogManager.saveTrieLog(localCopy, newWorldStateRootHash, blockHeader, this);
// not save a frozen state in the cache
if (!isFrozen) {
Expand Down
1 change: 1 addition & 0 deletions ethereum/referencetests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ dependencies {
implementation project(':crypto:algorithms')
implementation project(':datatypes')
implementation project(':ethereum:core')
implementation project(':metrics:core')
implementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
implementation project(':ethereum:rlp')
implementation project(':evm')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* 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.referencetests;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoader;
import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.bonsai.trielog.TrieLogAddedEvent;
import org.hyperledger.besu.ethereum.bonsai.trielog.TrieLogFactoryImpl;
import org.hyperledger.besu.ethereum.bonsai.trielog.TrieLogManager;
import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldState;
import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldStateUpdateAccumulator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.trielogs.TrieLog;
import org.hyperledger.besu.plugin.services.trielogs.TrieLogEvent;
import org.hyperledger.besu.plugin.services.trielogs.TrieLogFactory;
import org.hyperledger.besu.util.Subscribers;

import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import com.fasterxml.jackson.annotation.JsonCreator;

public class BonsaiReferenceTestWorldState extends BonsaiWorldState
implements ReferenceTestWorldState {

protected BonsaiReferenceTestWorldState(
final BonsaiWorldStateKeyValueStorage worldStateStorage,
final CachedMerkleTrieLoader cachedMerkleTrieLoader,
final TrieLogManager trieLogManager) {
super(worldStateStorage, cachedMerkleTrieLoader, trieLogManager);
}

@JsonCreator
public static BonsaiReferenceTestWorldState create(
final Map<String, ReferenceTestWorldState.AccountMock> accounts) {
final ObservableMetricsSystem metricsSystem = new NoOpMetricsSystem();
final CachedMerkleTrieLoader cachedMerkleTrieLoader = new CachedMerkleTrieLoader(metricsSystem);
final TrieLogManager trieLogManager = new NoOpTrieLogManager();
final BonsaiWorldStateKeyValueStorage worldStateStorage =
new BonsaiWorldStateKeyValueStorage(new InMemoryKeyValueStorageProvider(), metricsSystem);
final BonsaiReferenceTestWorldState worldState =
new BonsaiReferenceTestWorldState(
worldStateStorage, cachedMerkleTrieLoader, trieLogManager);

final WorldUpdater updater = worldState.updater();
for (final Map.Entry<String, ReferenceTestWorldState.AccountMock> entry : accounts.entrySet()) {
ReferenceTestWorldState.insertAccount(
updater, Address.fromHexString(entry.getKey()), entry.getValue());
}
updater.commit();
return worldState;
}

static class NoOpTrieLogManager implements TrieLogManager {
private final Subscribers<TrieLogEvent.TrieLogObserver> trieLogObservers = Subscribers.create();
private final TrieLogFactory trieLogFactory = new TrieLogFactoryImpl();

@Override
public void saveTrieLog(
final BonsaiWorldStateUpdateAccumulator localUpdater,
final Hash forWorldStateRootHash,
final BlockHeader forBlockHeader,
final BonsaiWorldState forWorldState) {
// notify trie log added observers, synchronously
TrieLog trieLog = trieLogFactory.create(localUpdater, forBlockHeader);
trieLogObservers.forEach(o -> o.onTrieLogAdded(new TrieLogAddedEvent(trieLog)));
}

@Override
public void addCachedLayer(
final BlockHeader blockHeader,
final Hash worldStateRootHash,
final BonsaiWorldState forWorldState) {}

@Override
public boolean containWorldStateStorage(final Hash blockHash) {
return false;
}

@Override
public Optional<BonsaiWorldState> getWorldState(final Hash blockHash) {
return Optional.empty();
}

@Override
public Optional<BonsaiWorldState> getNearestWorldState(final BlockHeader blockHeader) {
return Optional.empty();
}

@Override
public Optional<BonsaiWorldState> getHeadWorldState(
final Function<Hash, Optional<BlockHeader>> hashBlockHeaderFunction) {
return Optional.empty();
}

@Override
public long getMaxLayersToLoad() {
return 0;
}

@Override
public void reset() {}

@Override
public Optional<? extends TrieLog> getTrieLogLayer(final Hash blockHash) {
return Optional.empty();
}

@Override
public synchronized long subscribe(final TrieLogEvent.TrieLogObserver sub) {
return trieLogObservers.subscribe(sub);
}

@Override
public synchronized void unsubscribe(final long id) {
trieLogObservers.unsubscribe(id);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.referencetests;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;

import java.util.Map;

import com.fasterxml.jackson.annotation.JsonCreator;

class DefaultReferenceTestWorldState extends DefaultMutableWorldState
implements ReferenceTestWorldState {

DefaultReferenceTestWorldState() {
super(
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()),
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
}

@JsonCreator
static ReferenceTestWorldState create(final Map<String, AccountMock> accounts) {
final ReferenceTestWorldState worldState = new DefaultReferenceTestWorldState();
final WorldUpdater updater = worldState.updater();

for (final Map.Entry<String, AccountMock> entry : accounts.entrySet()) {
ReferenceTestWorldState.insertAccount(
updater, Address.fromHexString(entry.getKey()), entry.getValue());
}

updater.commit();
return worldState;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;

import java.util.HashMap;
import java.util.Map;
Expand All @@ -35,9 +32,9 @@

/** Represent a worldState for testing. */
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReferenceTestWorldState extends DefaultMutableWorldState {
public interface ReferenceTestWorldState extends MutableWorldState {

public static class AccountMock {
class AccountMock {
private final long nonce;
private final Wei balance;
private final Bytes code;
Expand Down Expand Up @@ -91,21 +88,8 @@ static void insertAccount(
}

@JsonCreator
public static ReferenceTestWorldState create(final Map<String, AccountMock> accounts) {
final ReferenceTestWorldState worldState = new ReferenceTestWorldState();
final WorldUpdater updater = worldState.updater();

for (final Map.Entry<String, AccountMock> entry : accounts.entrySet()) {
insertAccount(updater, Address.fromHexString(entry.getKey()), entry.getValue());
}

updater.commit();
return worldState;
}

public ReferenceTestWorldState() {
super(
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()),
new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage()));
static ReferenceTestWorldState create(final Map<String, AccountMock> accounts) {
// delegate to a Bonsai reference test world state:
return BonsaiReferenceTestWorldState.create(accounts);
}
}

0 comments on commit 37b2b07

Please sign in to comment.