mirror of https://github.com/hyperledger/besu
storage format refactor for preparing verkle trie integration (#6721)
Splitting Bonsai code: This commit aims to divide Bonsai into two packages - Common Classes with a Prefix: The common part will include classes prefixed with "DiffBased." These classes are designed to provide a base for both Bonsai and any future storage format that might use this diff-based approach. - Bonsai's Specifics: Bonsai will retain its unique features in its own package. This means that while it shares the diff-based infrastructure with the common part, it also has its own specific functionalities that are not shared with other storage format. - Extension to Verkle: this modification add the possibility of adding "Verkle" as a new storage format based on the diff-based architecture. Like Bonsai, Verkle would use the common diff-based classes but also have its own specific features. --------- Signed-off-by: Karim TAAM <karim.t2am@gmail.com> Signed-off-by: Karim Taam <karim.t2am@gmail.com>pull/6832/head develop
parent
7e46889817
commit
f2c2512ef6
@ -0,0 +1,145 @@ |
||||
/* |
||||
* 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.trie.diffbased.bonsai; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
import org.hyperledger.besu.ethereum.rlp.RLP; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.BonsaiCachedMerkleTrieLoader; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.BonsaiCachedWorldStorageManager; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedWorldStateProvider; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogManager; |
||||
import org.hyperledger.besu.ethereum.trie.patricia.StoredMerklePatriciaTrie; |
||||
import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue; |
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration; |
||||
import org.hyperledger.besu.plugin.BesuContext; |
||||
|
||||
import java.util.HashSet; |
||||
import java.util.Optional; |
||||
import java.util.Set; |
||||
import java.util.function.Function; |
||||
|
||||
import com.google.common.annotations.VisibleForTesting; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider { |
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BonsaiWorldStateProvider.class); |
||||
private final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader; |
||||
|
||||
public BonsaiWorldStateProvider( |
||||
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage, |
||||
final Blockchain blockchain, |
||||
final Optional<Long> maxLayersToLoad, |
||||
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader, |
||||
final BesuContext pluginContext, |
||||
final EvmConfiguration evmConfiguration) { |
||||
super(worldStateKeyValueStorage, blockchain, maxLayersToLoad, pluginContext); |
||||
this.bonsaiCachedMerkleTrieLoader = bonsaiCachedMerkleTrieLoader; |
||||
provideCachedWorldStorageManager( |
||||
new BonsaiCachedWorldStorageManager(this, worldStateKeyValueStorage)); |
||||
loadPersistedState(new BonsaiWorldState(this, worldStateKeyValueStorage, evmConfiguration)); |
||||
} |
||||
|
||||
@VisibleForTesting |
||||
BonsaiWorldStateProvider( |
||||
final BonsaiCachedWorldStorageManager bonsaiCachedWorldStorageManager, |
||||
final TrieLogManager trieLogManager, |
||||
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage, |
||||
final Blockchain blockchain, |
||||
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader, |
||||
final EvmConfiguration evmConfiguration) { |
||||
super(worldStateKeyValueStorage, blockchain, trieLogManager); |
||||
this.bonsaiCachedMerkleTrieLoader = bonsaiCachedMerkleTrieLoader; |
||||
provideCachedWorldStorageManager(bonsaiCachedWorldStorageManager); |
||||
loadPersistedState(new BonsaiWorldState(this, worldStateKeyValueStorage, evmConfiguration)); |
||||
} |
||||
|
||||
public BonsaiCachedMerkleTrieLoader getCachedMerkleTrieLoader() { |
||||
return bonsaiCachedMerkleTrieLoader; |
||||
} |
||||
|
||||
private BonsaiWorldStateKeyValueStorage getWorldStateKeyValueStorage() { |
||||
return (BonsaiWorldStateKeyValueStorage) worldStateKeyValueStorage; |
||||
} |
||||
/** |
||||
* Prepares the state healing process for a given address and location. It prepares the state |
||||
* healing, including retrieving data from storage, identifying invalid slots or nodes, removing |
||||
* account and slot from the state trie, and committing the changes. Finally, it downgrades the |
||||
* world state storage to partial flat database mode. |
||||
*/ |
||||
public void prepareStateHealing(final Address address, final Bytes location) { |
||||
final Set<Bytes> keysToDelete = new HashSet<>(); |
||||
final BonsaiWorldStateKeyValueStorage.Updater updater = |
||||
getWorldStateKeyValueStorage().updater(); |
||||
final Hash accountHash = address.addressHash(); |
||||
final StoredMerklePatriciaTrie<Bytes, Bytes> accountTrie = |
||||
new StoredMerklePatriciaTrie<>( |
||||
(l, h) -> { |
||||
final Optional<Bytes> node = |
||||
getWorldStateKeyValueStorage().getAccountStateTrieNode(l, h); |
||||
if (node.isPresent()) { |
||||
keysToDelete.add(l); |
||||
} |
||||
return node; |
||||
}, |
||||
persistedState.getWorldStateRootHash(), |
||||
Function.identity(), |
||||
Function.identity()); |
||||
try { |
||||
accountTrie |
||||
.get(accountHash) |
||||
.map(RLP::input) |
||||
.map(StateTrieAccountValue::readFrom) |
||||
.ifPresent( |
||||
account -> { |
||||
final StoredMerklePatriciaTrie<Bytes, Bytes> storageTrie = |
||||
new StoredMerklePatriciaTrie<>( |
||||
(l, h) -> { |
||||
Optional<Bytes> node = |
||||
getWorldStateKeyValueStorage() |
||||
.getAccountStorageTrieNode(accountHash, l, h); |
||||
if (node.isPresent()) { |
||||
keysToDelete.add(Bytes.concatenate(accountHash, l)); |
||||
} |
||||
return node; |
||||
}, |
||||
account.getStorageRoot(), |
||||
Function.identity(), |
||||
Function.identity()); |
||||
try { |
||||
storageTrie.getPath(location); |
||||
} catch (Exception eA) { |
||||
LOG.warn("Invalid slot found for account {} at location {}", address, location); |
||||
// ignore
|
||||
} |
||||
}); |
||||
} catch (Exception eA) { |
||||
LOG.warn("Invalid node for account {} at location {}", address, location); |
||||
// ignore
|
||||
} |
||||
keysToDelete.forEach(updater::removeAccountStateTrieNode); |
||||
updater.commit(); |
||||
|
||||
getWorldStateKeyValueStorage().downgradeToPartialFlatDbMode(); |
||||
} |
||||
} |
@ -0,0 +1,60 @@ |
||||
/* |
||||
* 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.trie.diffbased.bonsai.cache; |
||||
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiWorldStateProvider; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiSnapshotWorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateLayerStorage; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedWorldStateProvider; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.cache.DiffBasedCachedWorldStorageManager; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldState; |
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration; |
||||
|
||||
public class BonsaiCachedWorldStorageManager extends DiffBasedCachedWorldStorageManager { |
||||
|
||||
public BonsaiCachedWorldStorageManager( |
||||
final BonsaiWorldStateProvider archive, |
||||
final DiffBasedWorldStateKeyValueStorage worldStateKeyValueStorage) { |
||||
super(archive, worldStateKeyValueStorage); |
||||
} |
||||
|
||||
@Override |
||||
public DiffBasedWorldState createWorldState( |
||||
final DiffBasedWorldStateProvider archive, |
||||
final DiffBasedWorldStateKeyValueStorage worldStateKeyValueStorage, |
||||
final EvmConfiguration evmConfiguration) { |
||||
return new BonsaiWorldState( |
||||
(BonsaiWorldStateProvider) archive, |
||||
(BonsaiWorldStateKeyValueStorage) worldStateKeyValueStorage, |
||||
evmConfiguration); |
||||
} |
||||
|
||||
@Override |
||||
public DiffBasedWorldStateKeyValueStorage createLayeredKeyValueStorage( |
||||
final DiffBasedWorldStateKeyValueStorage worldStateKeyValueStorage) { |
||||
return new BonsaiWorldStateLayerStorage( |
||||
(BonsaiWorldStateKeyValueStorage) worldStateKeyValueStorage); |
||||
} |
||||
|
||||
@Override |
||||
public DiffBasedWorldStateKeyValueStorage createSnapshotKeyValueStorage( |
||||
final DiffBasedWorldStateKeyValueStorage worldStateKeyValueStorage) { |
||||
return new BonsaiSnapshotWorldStateKeyValueStorage( |
||||
(BonsaiWorldStateKeyValueStorage) worldStateKeyValueStorage); |
||||
} |
||||
} |
@ -0,0 +1,98 @@ |
||||
/* |
||||
* 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.trie.diffbased.bonsai.worldview; |
||||
|
||||
import org.hyperledger.besu.datatypes.AccountValue; |
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.datatypes.StorageSlotKey; |
||||
import org.hyperledger.besu.datatypes.Wei; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiAccount; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedValue; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldView; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.preload.Consumer; |
||||
import org.hyperledger.besu.evm.internal.EvmConfiguration; |
||||
import org.hyperledger.besu.evm.worldstate.UpdateTrackingAccount; |
||||
|
||||
public class BonsaiWorldStateUpdateAccumulator |
||||
extends DiffBasedWorldStateUpdateAccumulator<BonsaiAccount> { |
||||
public BonsaiWorldStateUpdateAccumulator( |
||||
final DiffBasedWorldView world, |
||||
final Consumer<DiffBasedValue<BonsaiAccount>> accountPreloader, |
||||
final Consumer<StorageSlotKey> storagePreloader, |
||||
final EvmConfiguration evmConfiguration) { |
||||
super(world, accountPreloader, storagePreloader, evmConfiguration); |
||||
} |
||||
|
||||
@Override |
||||
public DiffBasedWorldStateUpdateAccumulator<BonsaiAccount> copy() { |
||||
final BonsaiWorldStateUpdateAccumulator copy = |
||||
new BonsaiWorldStateUpdateAccumulator( |
||||
wrappedWorldView(), |
||||
getAccountPreloader(), |
||||
getStoragePreloader(), |
||||
getEvmConfiguration()); |
||||
copy.cloneFromUpdater(this); |
||||
return copy; |
||||
} |
||||
|
||||
@Override |
||||
protected BonsaiAccount copyAccount(final BonsaiAccount account) { |
||||
return new BonsaiAccount(account); |
||||
} |
||||
|
||||
@Override |
||||
protected BonsaiAccount copyAccount( |
||||
final BonsaiAccount toCopy, final DiffBasedWorldView context, final boolean mutable) { |
||||
return new BonsaiAccount(toCopy, context, mutable); |
||||
} |
||||
|
||||
@Override |
||||
protected BonsaiAccount createAccount( |
||||
final DiffBasedWorldView context, |
||||
final Address address, |
||||
final AccountValue stateTrieAccount, |
||||
final boolean mutable) { |
||||
return new BonsaiAccount(context, address, stateTrieAccount, mutable); |
||||
} |
||||
|
||||
@Override |
||||
protected BonsaiAccount createAccount( |
||||
final DiffBasedWorldView context, |
||||
final Address address, |
||||
final Hash addressHash, |
||||
final long nonce, |
||||
final Wei balance, |
||||
final Hash storageRoot, |
||||
final Hash codeHash, |
||||
final boolean mutable) { |
||||
return new BonsaiAccount( |
||||
context, address, addressHash, nonce, balance, storageRoot, codeHash, mutable); |
||||
} |
||||
|
||||
@Override |
||||
protected BonsaiAccount createAccount( |
||||
final DiffBasedWorldView context, final UpdateTrackingAccount<BonsaiAccount> tracked) { |
||||
return new BonsaiAccount(context, tracked); |
||||
} |
||||
|
||||
@Override |
||||
protected void assertCloseEnoughForDiffing( |
||||
final BonsaiAccount source, final AccountValue account, final String context) { |
||||
BonsaiAccount.assertCloseEnoughForDiffing(source, account, context); |
||||
} |
||||
} |
@ -0,0 +1,206 @@ |
||||
/* |
||||
* 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.trie.diffbased.common; |
||||
|
||||
import org.hyperledger.besu.datatypes.AccountValue; |
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.datatypes.Wei; |
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldView; |
||||
import org.hyperledger.besu.evm.ModificationNotAllowedException; |
||||
import org.hyperledger.besu.evm.account.MutableAccount; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
|
||||
public abstract class DiffBasedAccount implements MutableAccount, AccountValue { |
||||
protected final DiffBasedWorldView context; |
||||
protected boolean immutable; |
||||
protected final Address address; |
||||
protected final Hash addressHash; |
||||
protected Hash codeHash; |
||||
protected long nonce; |
||||
protected Wei balance; |
||||
protected Bytes code; |
||||
|
||||
protected final Map<UInt256, UInt256> updatedStorage = new HashMap<>(); |
||||
|
||||
public DiffBasedAccount( |
||||
final DiffBasedWorldView context, |
||||
final Address address, |
||||
final Hash addressHash, |
||||
final long nonce, |
||||
final Wei balance, |
||||
final Hash codeHash, |
||||
final boolean mutable) { |
||||
this.context = context; |
||||
this.address = address; |
||||
this.addressHash = addressHash; |
||||
this.nonce = nonce; |
||||
this.balance = balance; |
||||
this.codeHash = codeHash; |
||||
|
||||
this.immutable = !mutable; |
||||
} |
||||
|
||||
public DiffBasedAccount( |
||||
final DiffBasedWorldView context, |
||||
final Address address, |
||||
final AccountValue stateTrieAccount, |
||||
final boolean mutable) { |
||||
this( |
||||
context, |
||||
address, |
||||
address.addressHash(), |
||||
stateTrieAccount.getNonce(), |
||||
stateTrieAccount.getBalance(), |
||||
stateTrieAccount.getCodeHash(), |
||||
mutable); |
||||
} |
||||
|
||||
public DiffBasedAccount( |
||||
final DiffBasedAccount toCopy, final DiffBasedWorldView context, final boolean mutable) { |
||||
this.context = context; |
||||
this.address = toCopy.address; |
||||
this.addressHash = toCopy.addressHash; |
||||
this.nonce = toCopy.nonce; |
||||
this.balance = toCopy.balance; |
||||
this.codeHash = toCopy.codeHash; |
||||
this.code = toCopy.code; |
||||
updatedStorage.putAll(toCopy.updatedStorage); |
||||
|
||||
this.immutable = !mutable; |
||||
} |
||||
|
||||
@Override |
||||
public Address getAddress() { |
||||
return address; |
||||
} |
||||
|
||||
@Override |
||||
public Hash getAddressHash() { |
||||
return addressHash; |
||||
} |
||||
|
||||
@Override |
||||
public long getNonce() { |
||||
return nonce; |
||||
} |
||||
|
||||
@Override |
||||
public void setNonce(final long value) { |
||||
if (immutable) { |
||||
throw new ModificationNotAllowedException(); |
||||
} |
||||
nonce = value; |
||||
} |
||||
|
||||
@Override |
||||
public Wei getBalance() { |
||||
return balance; |
||||
} |
||||
|
||||
@Override |
||||
public void setBalance(final Wei value) { |
||||
if (immutable) { |
||||
throw new ModificationNotAllowedException(); |
||||
} |
||||
balance = value; |
||||
} |
||||
|
||||
@Override |
||||
public Bytes getCode() { |
||||
if (code == null) { |
||||
code = context.getCode(address, codeHash).orElse(Bytes.EMPTY); |
||||
} |
||||
return code; |
||||
} |
||||
|
||||
@Override |
||||
public void setCode(final Bytes code) { |
||||
if (immutable) { |
||||
throw new ModificationNotAllowedException(); |
||||
} |
||||
this.code = code; |
||||
if (code == null || code.isEmpty()) { |
||||
this.codeHash = Hash.EMPTY; |
||||
} else { |
||||
this.codeHash = Hash.hash(code); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Hash getCodeHash() { |
||||
return codeHash; |
||||
} |
||||
|
||||
@Override |
||||
public UInt256 getStorageValue(final UInt256 key) { |
||||
return context.getStorageValue(address, key); |
||||
} |
||||
|
||||
@Override |
||||
public UInt256 getOriginalStorageValue(final UInt256 key) { |
||||
return context.getPriorStorageValue(address, key); |
||||
} |
||||
|
||||
public Bytes serializeAccount() { |
||||
final BytesValueRLPOutput out = new BytesValueRLPOutput(); |
||||
writeTo(out); |
||||
return out.encoded(); |
||||
} |
||||
|
||||
@Override |
||||
public void setStorageValue(final UInt256 key, final UInt256 value) { |
||||
if (immutable) { |
||||
throw new ModificationNotAllowedException(); |
||||
} |
||||
updatedStorage.put(key, value); |
||||
} |
||||
|
||||
@Override |
||||
public void clearStorage() { |
||||
updatedStorage.clear(); |
||||
} |
||||
|
||||
@Override |
||||
public Map<UInt256, UInt256> getUpdatedStorage() { |
||||
return updatedStorage; |
||||
} |
||||
|
||||
@Override |
||||
public void becomeImmutable() { |
||||
immutable = true; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "AccountState{" |
||||
+ "address=" |
||||
+ address |
||||
+ ", nonce=" |
||||
+ nonce |
||||
+ ", balance=" |
||||
+ balance |
||||
+ ", codeHash=" |
||||
+ codeHash |
||||
+ '}'; |
||||
} |
||||
} |
@ -0,0 +1,25 @@ |
||||
/* |
||||
* 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.trie.diffbased.common; |
||||
|
||||
public interface StorageSubscriber { |
||||
default void onClearStorage() {} |
||||
|
||||
default void onClearFlatDatabaseStorage() {} |
||||
|
||||
default void onClearTrieLog() {} |
||||
|
||||
default void onCloseStorage() {} |
||||
} |
@ -0,0 +1,22 @@ |
||||
/* |
||||
* 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.trie.diffbased.common.storage; |
||||
|
||||
public interface DiffBasedLayeredWorldStateKeyValueStorage |
||||
extends DiffBasedSnapshotWorldStateKeyValueStorage { |
||||
|
||||
DiffBasedWorldStateKeyValueStorage clone(); |
||||
} |
@ -0,0 +1,21 @@ |
||||
/* |
||||
* 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.trie.diffbased.common.storage; |
||||
|
||||
public interface DiffBasedSnapshotWorldStateKeyValueStorage { |
||||
|
||||
DiffBasedWorldStateKeyValueStorage getParentWorldStateStorage(); |
||||
} |
@ -0,0 +1,234 @@ |
||||
/* |
||||
* 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.trie.diffbased.common.storage; |
||||
|
||||
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE; |
||||
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE; |
||||
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.CODE_STORAGE; |
||||
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE; |
||||
|
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.storage.StorageProvider; |
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.StorageSubscriber; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.flat.FlatDbStrategy; |
||||
import org.hyperledger.besu.ethereum.worldstate.FlatDbMode; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; |
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; |
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; |
||||
import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorage; |
||||
import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction; |
||||
import org.hyperledger.besu.util.Subscribers; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
import java.util.stream.Stream; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public abstract class DiffBasedWorldStateKeyValueStorage |
||||
implements WorldStateKeyValueStorage, AutoCloseable { |
||||
private static final Logger LOG = |
||||
LoggerFactory.getLogger(DiffBasedWorldStateKeyValueStorage.class); |
||||
|
||||
// 0x776f726c64526f6f74
|
||||
public static final byte[] WORLD_ROOT_HASH_KEY = "worldRoot".getBytes(StandardCharsets.UTF_8); |
||||
// 0x776f726c64426c6f636b48617368
|
||||
public static final byte[] WORLD_BLOCK_HASH_KEY = |
||||
"worldBlockHash".getBytes(StandardCharsets.UTF_8); |
||||
|
||||
private final AtomicBoolean shouldClose = new AtomicBoolean(false); |
||||
|
||||
protected final AtomicBoolean isClosed = new AtomicBoolean(false); |
||||
|
||||
protected final Subscribers<StorageSubscriber> subscribers = Subscribers.create(); |
||||
protected final SegmentedKeyValueStorage composedWorldStateStorage; |
||||
protected final KeyValueStorage trieLogStorage; |
||||
|
||||
public DiffBasedWorldStateKeyValueStorage(final StorageProvider provider) { |
||||
this.composedWorldStateStorage = |
||||
provider.getStorageBySegmentIdentifiers( |
||||
List.of( |
||||
ACCOUNT_INFO_STATE, CODE_STORAGE, ACCOUNT_STORAGE_STORAGE, TRIE_BRANCH_STORAGE)); |
||||
this.trieLogStorage = |
||||
provider.getStorageBySegmentIdentifier(KeyValueSegmentIdentifier.TRIE_LOG_STORAGE); |
||||
} |
||||
|
||||
public DiffBasedWorldStateKeyValueStorage( |
||||
final SegmentedKeyValueStorage composedWorldStateStorage, |
||||
final KeyValueStorage trieLogStorage) { |
||||
this.composedWorldStateStorage = composedWorldStateStorage; |
||||
this.trieLogStorage = trieLogStorage; |
||||
} |
||||
|
||||
public abstract FlatDbMode getFlatDbMode(); |
||||
|
||||
public abstract FlatDbStrategy getFlatDbStrategy(); |
||||
|
||||
@Override |
||||
public abstract DataStorageFormat getDataStorageFormat(); |
||||
|
||||
public SegmentedKeyValueStorage getComposedWorldStateStorage() { |
||||
return composedWorldStateStorage; |
||||
} |
||||
|
||||
public KeyValueStorage getTrieLogStorage() { |
||||
return trieLogStorage; |
||||
} |
||||
|
||||
public Optional<byte[]> getTrieLog(final Hash blockHash) { |
||||
return trieLogStorage.get(blockHash.toArrayUnsafe()); |
||||
} |
||||
|
||||
public Stream<byte[]> streamTrieLogKeys(final long limit) { |
||||
return trieLogStorage.streamKeys().limit(limit); |
||||
} |
||||
|
||||
public Optional<Bytes> getStateTrieNode(final Bytes location) { |
||||
return composedWorldStateStorage |
||||
.get(TRIE_BRANCH_STORAGE, location.toArrayUnsafe()) |
||||
.map(Bytes::wrap); |
||||
} |
||||
|
||||
public Optional<Bytes> getWorldStateRootHash() { |
||||
return composedWorldStateStorage.get(TRIE_BRANCH_STORAGE, WORLD_ROOT_HASH_KEY).map(Bytes::wrap); |
||||
} |
||||
|
||||
public Optional<Hash> getWorldStateBlockHash() { |
||||
return composedWorldStateStorage |
||||
.get(TRIE_BRANCH_STORAGE, WORLD_BLOCK_HASH_KEY) |
||||
.map(Bytes32::wrap) |
||||
.map(Hash::wrap); |
||||
} |
||||
|
||||
public Map<Bytes32, Bytes> streamFlatAccounts( |
||||
final Bytes startKeyHash, final Bytes32 endKeyHash, final long max) { |
||||
return getFlatDbStrategy() |
||||
.streamAccountFlatDatabase(composedWorldStateStorage, startKeyHash, endKeyHash, max); |
||||
} |
||||
|
||||
public Map<Bytes32, Bytes> streamFlatStorages( |
||||
final Hash accountHash, final Bytes startKeyHash, final Bytes32 endKeyHash, final long max) { |
||||
return getFlatDbStrategy() |
||||
.streamStorageFlatDatabase( |
||||
composedWorldStateStorage, accountHash, startKeyHash, endKeyHash, max); |
||||
} |
||||
|
||||
public boolean isWorldStateAvailable(final Bytes32 rootHash, final Hash blockHash) { |
||||
return composedWorldStateStorage |
||||
.get(TRIE_BRANCH_STORAGE, WORLD_ROOT_HASH_KEY) |
||||
.map(Bytes32::wrap) |
||||
.map(hash -> hash.equals(rootHash) || trieLogStorage.containsKey(blockHash.toArrayUnsafe())) |
||||
.orElse(false); |
||||
} |
||||
|
||||
@Override |
||||
public void clear() { |
||||
subscribers.forEach(StorageSubscriber::onClearStorage); |
||||
getFlatDbStrategy().clearAll(composedWorldStateStorage); |
||||
composedWorldStateStorage.clear(TRIE_BRANCH_STORAGE); |
||||
trieLogStorage.clear(); |
||||
} |
||||
|
||||
public void clearTrieLog() { |
||||
subscribers.forEach(StorageSubscriber::onClearTrieLog); |
||||
trieLogStorage.clear(); |
||||
} |
||||
|
||||
public void clearFlatDatabase() { |
||||
subscribers.forEach(StorageSubscriber::onClearFlatDatabaseStorage); |
||||
getFlatDbStrategy().resetOnResync(composedWorldStateStorage); |
||||
} |
||||
|
||||
@Override |
||||
public abstract Updater updater(); |
||||
|
||||
public boolean pruneTrieLog(final Hash blockHash) { |
||||
try { |
||||
return trieLogStorage.tryDelete(blockHash.toArrayUnsafe()); |
||||
} catch (Exception e) { |
||||
LOG.error("Error pruning trie log for block hash {}", blockHash, e); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public synchronized void close() throws Exception { |
||||
// when the storage clears, close
|
||||
shouldClose.set(true); |
||||
tryClose(); |
||||
} |
||||
|
||||
public synchronized long subscribe(final StorageSubscriber sub) { |
||||
if (isClosed.get()) { |
||||
throw new RuntimeException("Storage is marked to close or has already closed"); |
||||
} |
||||
return subscribers.subscribe(sub); |
||||
} |
||||
|
||||
public synchronized void unSubscribe(final long id) { |
||||
subscribers.unsubscribe(id); |
||||
try { |
||||
tryClose(); |
||||
} catch (Exception e) { |
||||
LOG.atWarn() |
||||
.setMessage("exception while trying to close : {}") |
||||
.addArgument(e::getMessage) |
||||
.log(); |
||||
} |
||||
} |
||||
|
||||
protected synchronized void tryClose() throws Exception { |
||||
if (shouldClose.get() && subscribers.getSubscriberCount() < 1) { |
||||
doClose(); |
||||
} |
||||
} |
||||
|
||||
protected synchronized void doClose() throws Exception { |
||||
if (!isClosed.get()) { |
||||
// alert any subscribers we are closing:
|
||||
subscribers.forEach(StorageSubscriber::onCloseStorage); |
||||
|
||||
// close all of the KeyValueStorages:
|
||||
composedWorldStateStorage.close(); |
||||
trieLogStorage.close(); |
||||
|
||||
// set storage closed
|
||||
isClosed.set(true); |
||||
} |
||||
} |
||||
|
||||
public interface Updater extends WorldStateKeyValueStorage.Updater { |
||||
|
||||
DiffBasedWorldStateKeyValueStorage.Updater saveWorldState( |
||||
final Bytes blockHash, final Bytes32 nodeHash, final Bytes node); |
||||
|
||||
SegmentedKeyValueStorageTransaction getWorldStateTransaction(); |
||||
|
||||
KeyValueStorageTransaction getTrieLogStorageTransaction(); |
||||
|
||||
@Override |
||||
void commit(); |
||||
|
||||
void rollback(); |
||||
} |
||||
} |
@ -0,0 +1,334 @@ |
||||
/* |
||||
* 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.trie.diffbased.common.worldview; |
||||
|
||||
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE; |
||||
import static org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage.WORLD_BLOCK_HASH_KEY; |
||||
import static org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage.WORLD_ROOT_HASH_KEY; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
import org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.datatypes.StorageSlotKey; |
||||
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.StorageSubscriber; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.cache.DiffBasedCachedWorldStorageManager; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedLayeredWorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedSnapshotWorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogManager; |
||||
import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateKeyValueStorage; |
||||
import org.hyperledger.besu.evm.account.Account; |
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater; |
||||
import org.hyperledger.besu.plugin.services.exception.StorageException; |
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; |
||||
import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier; |
||||
import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction; |
||||
|
||||
import java.util.Optional; |
||||
import java.util.stream.Stream; |
||||
import javax.annotation.Nonnull; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.apache.tuweni.bytes.Bytes32; |
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
public abstract class DiffBasedWorldState |
||||
implements MutableWorldState, DiffBasedWorldView, StorageSubscriber { |
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DiffBasedWorldState.class); |
||||
|
||||
protected DiffBasedWorldStateKeyValueStorage worldStateKeyValueStorage; |
||||
protected final DiffBasedCachedWorldStorageManager cachedWorldStorageManager; |
||||
protected final TrieLogManager trieLogManager; |
||||
protected DiffBasedWorldStateUpdateAccumulator<?> accumulator; |
||||
|
||||
protected Hash worldStateRootHash; |
||||
protected Hash worldStateBlockHash; |
||||
protected boolean isFrozen; |
||||
|
||||
protected DiffBasedWorldState( |
||||
final DiffBasedWorldStateKeyValueStorage worldStateKeyValueStorage, |
||||
final DiffBasedCachedWorldStorageManager cachedWorldStorageManager, |
||||
final TrieLogManager trieLogManager) { |
||||
this.worldStateKeyValueStorage = worldStateKeyValueStorage; |
||||
this.worldStateRootHash = |
||||
Hash.wrap( |
||||
Bytes32.wrap( |
||||
worldStateKeyValueStorage.getWorldStateRootHash().orElse(getEmptyTrieHash()))); |
||||
this.worldStateBlockHash = |
||||
Hash.wrap( |
||||
Bytes32.wrap(worldStateKeyValueStorage.getWorldStateBlockHash().orElse(Hash.ZERO))); |
||||
this.cachedWorldStorageManager = cachedWorldStorageManager; |
||||
this.trieLogManager = trieLogManager; |
||||
} |
||||
|
||||
/** |
||||
* Having a protected method to override the accumulator solves the chicken-egg problem of needing |
||||
* a worldstate reference (this) when construction the Accumulator. |
||||
* |
||||
* @param accumulator accumulator to use. |
||||
*/ |
||||
public void setAccumulator(final DiffBasedWorldStateUpdateAccumulator<?> accumulator) { |
||||
this.accumulator = accumulator; |
||||
} |
||||
|
||||
/** |
||||
* Returns the world state block hash of this world state |
||||
* |
||||
* @return the world state block hash. |
||||
*/ |
||||
public Hash getWorldStateBlockHash() { |
||||
return worldStateBlockHash; |
||||
} |
||||
|
||||
/** |
||||
* Returns the world state root hash of this world state |
||||
* |
||||
* @return the world state root hash. |
||||
*/ |
||||
public Hash getWorldStateRootHash() { |
||||
return worldStateRootHash; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isPersisted() { |
||||
return isPersisted(worldStateKeyValueStorage); |
||||
} |
||||
|
||||
private boolean isPersisted(final WorldStateKeyValueStorage worldStateKeyValueStorage) { |
||||
return !(worldStateKeyValueStorage instanceof DiffBasedSnapshotWorldStateKeyValueStorage); |
||||
} |
||||
|
||||
/** |
||||
* Reset the worldState to this block header |
||||
* |
||||
* @param blockHeader block to use |
||||
*/ |
||||
public void resetWorldStateTo(final BlockHeader blockHeader) { |
||||
worldStateBlockHash = blockHeader.getBlockHash(); |
||||
worldStateRootHash = blockHeader.getStateRoot(); |
||||
} |
||||
|
||||
@Override |
||||
public DiffBasedWorldStateKeyValueStorage getWorldStateStorage() { |
||||
return worldStateKeyValueStorage; |
||||
} |
||||
|
||||
public DiffBasedWorldStateUpdateAccumulator<?> getAccumulator() { |
||||
return accumulator; |
||||
} |
||||
|
||||
@Override |
||||
public void persist(final BlockHeader blockHeader) { |
||||
final Optional<BlockHeader> maybeBlockHeader = Optional.ofNullable(blockHeader); |
||||
LOG.atDebug() |
||||
.setMessage("Persist world state for block {}") |
||||
.addArgument(maybeBlockHeader) |
||||
.log(); |
||||
|
||||
final DiffBasedWorldStateUpdateAccumulator<?> localCopy = accumulator.copy(); |
||||
|
||||
boolean success = false; |
||||
|
||||
final DiffBasedWorldStateKeyValueStorage.Updater stateUpdater = |
||||
worldStateKeyValueStorage.updater(); |
||||
Runnable saveTrieLog = () -> {}; |
||||
|
||||
try { |
||||
final Hash newWorldStateRootHash = |
||||
calculateRootHash(isFrozen ? Optional.empty() : Optional.of(stateUpdater), accumulator); |
||||
// if we are persisted with a block header, and the prior state is the parent
|
||||
// then persist the TrieLog for that transition.
|
||||
// If specified but not a direct descendant simply store the new block hash.
|
||||
if (blockHeader != null) { |
||||
verifyWorldStateRoot(newWorldStateRootHash, blockHeader); |
||||
saveTrieLog = |
||||
() -> { |
||||
trieLogManager.saveTrieLog(localCopy, newWorldStateRootHash, blockHeader, this); |
||||
// not save a frozen state in the cache
|
||||
if (!isFrozen) { |
||||
cachedWorldStorageManager.addCachedLayer(blockHeader, newWorldStateRootHash, this); |
||||
} |
||||
}; |
||||
|
||||
stateUpdater |
||||
.getWorldStateTransaction() |
||||
.put(TRIE_BRANCH_STORAGE, WORLD_BLOCK_HASH_KEY, blockHeader.getHash().toArrayUnsafe()); |
||||
worldStateBlockHash = blockHeader.getHash(); |
||||
} else { |
||||
stateUpdater.getWorldStateTransaction().remove(TRIE_BRANCH_STORAGE, WORLD_BLOCK_HASH_KEY); |
||||
worldStateBlockHash = null; |
||||
} |
||||
|
||||
stateUpdater |
||||
.getWorldStateTransaction() |
||||
.put(TRIE_BRANCH_STORAGE, WORLD_ROOT_HASH_KEY, newWorldStateRootHash.toArrayUnsafe()); |
||||
worldStateRootHash = newWorldStateRootHash; |
||||
success = true; |
||||
} finally { |
||||
if (success) { |
||||
stateUpdater.commit(); |
||||
accumulator.reset(); |
||||
saveTrieLog.run(); |
||||
} else { |
||||
stateUpdater.rollback(); |
||||
accumulator.reset(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected void verifyWorldStateRoot(final Hash calculatedStateRoot, final BlockHeader header) { |
||||
if (!calculatedStateRoot.equals(header.getStateRoot())) { |
||||
throw new RuntimeException( |
||||
"World State Root does not match expected value, header " |
||||
+ header.getStateRoot().toHexString() |
||||
+ " calculated " |
||||
+ calculatedStateRoot.toHexString()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public WorldUpdater updater() { |
||||
return accumulator; |
||||
} |
||||
|
||||
@Override |
||||
public Hash rootHash() { |
||||
if (isFrozen && accumulator.isAccumulatorStateChanged()) { |
||||
worldStateRootHash = calculateRootHash(Optional.empty(), accumulator.copy()); |
||||
accumulator.resetAccumulatorStateChanged(); |
||||
} |
||||
return Hash.wrap(worldStateRootHash); |
||||
} |
||||
|
||||
protected static final KeyValueStorageTransaction noOpTx = |
||||
new KeyValueStorageTransaction() { |
||||
|
||||
@Override |
||||
public void put(final byte[] key, final byte[] value) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void remove(final byte[] key) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void commit() throws StorageException { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void rollback() { |
||||
// no-op
|
||||
} |
||||
}; |
||||
|
||||
protected static final SegmentedKeyValueStorageTransaction noOpSegmentedTx = |
||||
new SegmentedKeyValueStorageTransaction() { |
||||
|
||||
@Override |
||||
public void put( |
||||
final SegmentIdentifier segmentIdentifier, final byte[] key, final byte[] value) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void remove(final SegmentIdentifier segmentIdentifier, final byte[] key) { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void commit() throws StorageException { |
||||
// no-op
|
||||
} |
||||
|
||||
@Override |
||||
public void rollback() { |
||||
// no-op
|
||||
} |
||||
}; |
||||
|
||||
public Hash blockHash() { |
||||
return worldStateBlockHash; |
||||
} |
||||
|
||||
@Override |
||||
public Stream<StreamableAccount> streamAccounts(final Bytes32 startKeyHash, final int limit) { |
||||
throw new RuntimeException("storage format do not provide account streaming."); |
||||
} |
||||
|
||||
@Override |
||||
public UInt256 getPriorStorageValue(final Address address, final UInt256 storageKey) { |
||||
return getStorageValue(address, storageKey); |
||||
} |
||||
|
||||
@Override |
||||
public void close() { |
||||
try { |
||||
if (!isPersisted()) { |
||||
this.worldStateKeyValueStorage.close(); |
||||
if (isFrozen) { |
||||
closeFrozenStorage(); |
||||
} |
||||
} |
||||
} catch (Exception e) { |
||||
// no op
|
||||
} |
||||
} |
||||
|
||||
private void closeFrozenStorage() { |
||||
try { |
||||
final DiffBasedLayeredWorldStateKeyValueStorage worldStateLayerStorage = |
||||
(DiffBasedLayeredWorldStateKeyValueStorage) worldStateKeyValueStorage; |
||||
if (!isPersisted(worldStateLayerStorage.getParentWorldStateStorage())) { |
||||
worldStateLayerStorage.getParentWorldStateStorage().close(); |
||||
} |
||||
} catch (Exception e) { |
||||
// no op
|
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public abstract Hash frontierRootHash(); |
||||
|
||||
@Override |
||||
public abstract MutableWorldState freeze(); |
||||
|
||||
@Override |
||||
public abstract Account get(final Address address); |
||||
|
||||
@Override |
||||
public abstract UInt256 getStorageValue(final Address address, final UInt256 storageKey); |
||||
|
||||
@Override |
||||
public abstract Optional<UInt256> getStorageValueByStorageSlotKey( |
||||
final Address address, final StorageSlotKey storageSlotKey); |
||||
|
||||
@Override |
||||
public abstract Optional<Bytes> getCode(@Nonnull final Address address, final Hash codeHash); |
||||
|
||||
protected abstract Hash calculateRootHash( |
||||
final Optional<DiffBasedWorldStateKeyValueStorage.Updater> maybeStateUpdater, |
||||
final DiffBasedWorldStateUpdateAccumulator<?> worldStateUpdater); |
||||
|
||||
protected abstract Hash getEmptyTrieHash(); |
||||
} |
@ -0,0 +1,49 @@ |
||||
/* |
||||
* 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.trie.diffbased.common.worldview.accumulator.preload; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
|
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentMap; |
||||
import javax.annotation.Nonnull; |
||||
|
||||
import com.google.common.collect.ForwardingMap; |
||||
|
||||
public class AccountConsumingMap<T> extends ForwardingMap<Address, T> { |
||||
|
||||
private final ConcurrentMap<Address, T> accounts; |
||||
private final Consumer<T> consumer; |
||||
|
||||
public AccountConsumingMap(final ConcurrentMap<Address, T> accounts, final Consumer<T> consumer) { |
||||
this.accounts = accounts; |
||||
this.consumer = consumer; |
||||
} |
||||
|
||||
@Override |
||||
public T put(@Nonnull final Address address, @Nonnull final T value) { |
||||
consumer.process(address, value); |
||||
return accounts.put(address, value); |
||||
} |
||||
|
||||
public Consumer<T> getConsumer() { |
||||
return consumer; |
||||
} |
||||
|
||||
@Override |
||||
protected Map<Address, T> delegate() { |
||||
return accounts; |
||||
} |
||||
} |
@ -0,0 +1,21 @@ |
||||
/* |
||||
* 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.trie.diffbased.common.worldview.accumulator.preload; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
|
||||
public interface Consumer<T> { |
||||
void process(final Address address, T value); |
||||
} |
@ -0,0 +1,53 @@ |
||||
/* |
||||
* 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.trie.diffbased.common.worldview.accumulator.preload; |
||||
|
||||
import org.hyperledger.besu.datatypes.Address; |
||||
|
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentMap; |
||||
import javax.annotation.Nonnull; |
||||
|
||||
import com.google.common.collect.ForwardingMap; |
||||
|
||||
public class StorageConsumingMap<K, T> extends ForwardingMap<K, T> { |
||||
|
||||
private final Address address; |
||||
|
||||
private final ConcurrentMap<K, T> storages; |
||||
private final Consumer<K> consumer; |
||||
|
||||
public StorageConsumingMap( |
||||
final Address address, final ConcurrentMap<K, T> storages, final Consumer<K> consumer) { |
||||
this.address = address; |
||||
this.storages = storages; |
||||
this.consumer = consumer; |
||||
} |
||||
|
||||
@Override |
||||
public T put(@Nonnull final K slotKey, @Nonnull final T value) { |
||||
consumer.process(address, slotKey); |
||||
return storages.put(slotKey, value); |
||||
} |
||||
|
||||
public Consumer<K> getConsumer() { |
||||
return consumer; |
||||
} |
||||
|
||||
@Override |
||||
protected Map<K, T> delegate() { |
||||
return storages; |
||||
} |
||||
} |
Loading…
Reference in new issue