diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/VerkleAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/VerkleAccount.java
index 692dd19f731..bd7170dd71e 100644
--- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/VerkleAccount.java
+++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/VerkleAccount.java
@@ -37,6 +37,7 @@
public class VerkleAccount extends DiffBasedAccount {
private Hash storageRoot; // TODO REMOVE AS USELESS
+ private int hashCode;
public VerkleAccount(
final DiffBasedWorldView context,
@@ -180,4 +181,33 @@ public static void assertCloseEnoughForDiffing(
public boolean isStorageEmpty() {
return true; // TODO need to find a way to manage that with verkle
}
+
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ } else if (!(other instanceof VerkleAccount)) {
+ return false;
+ }
+ VerkleAccount otherVerkleAccount = (VerkleAccount) other;
+ return Objects.equals(this.address, otherVerkleAccount.address)
+ && this.nonce == otherVerkleAccount.nonce
+ && Objects.equals(this.balance, otherVerkleAccount.balance)
+ && Objects.equals(this.codeHash, otherVerkleAccount.codeHash);
+ }
+
+ @Override
+ public int hashCode() {
+ if (!immutable) {
+ return computeHashCode();
+ }
+ if (hashCode == 0) {
+ hashCode = computeHashCode();
+ }
+ return hashCode;
+ }
+
+ private int computeHashCode() {
+ return Objects.hash(address, nonce, balance, codeHash);
+ }
}
diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java
index 3e3bf89bd1c..38b0cfc74ac 100644
--- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java
+++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java
@@ -44,6 +44,7 @@
import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -51,7 +52,6 @@
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
-import kotlin.Pair;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
@@ -120,13 +120,17 @@ protected Hash internalCalculateRootHash(
final Map
preloadedHashers = new ConcurrentHashMap<>();
- worldStateUpdater.getAccountsToUpdate().entrySet().parallelStream()
+ final Set addressesToPersist = getAddressesToPersist(worldStateUpdater);
+ addressesToPersist.parallelStream()
.forEach(
- accountUpdate -> {
- final Address accountKey = accountUpdate.getKey();
+ accountKey -> {
+ final DiffBasedValue accountUpdate =
+ worldStateUpdater.getAccountsToUpdate().get(accountKey);
// generate account triekeys
- final List accountKeyIds =
- new ArrayList<>(trieKeyPreloader.generateAccountKeyIds());
+ final List accountKeyIds = new ArrayList<>();
+ if (accountUpdate != null && !accountUpdate.isUnchanged()) {
+ accountKeyIds.add(trieKeyPreloader.generateAccountKeyId());
+ }
// generate storage triekeys
final List storageKeyIds = new ArrayList<>();
@@ -153,7 +157,7 @@ protected Hash internalCalculateRootHash(
!codeUpdate.isUnchanged()
&& !(codeIsEmpty(previousCode) && codeIsEmpty(updatedCode));
if (isCodeUpdateNeeded) {
- accountKeyIds.add(Parameters.CODE_SIZE_LEAF_KEY);
+ accountKeyIds.add(Parameters.CODE_HASH_LEAF_KEY);
codeKeyIds.addAll(
trieKeyPreloader.generateCodeChunkKeyIds(
updatedCode == null ? previousCode : updatedCode));
@@ -166,24 +170,13 @@ protected Hash internalCalculateRootHash(
accountKey, accountKeyIds, storageKeyIds, codeKeyIds));
});
- for (final Map.Entry> accountUpdate :
- worldStateUpdater.getAccountsToUpdate().entrySet()) {
- final Address accountKey = accountUpdate.getKey();
- final HasherContext hasherContext = preloadedHashers.get(accountKey);
- final VerkleEntryFactory verkleEntryFactory = new VerkleEntryFactory(hasherContext.hasher());
- if (hasherContext.hasStorageTrieKeys()) {
- final StorageConsumingMap> storageAccountUpdate =
- worldStateUpdater.getStorageToUpdate().get(accountKey);
- updateAccountStorageState(
- accountKey, stateTrie, maybeStateUpdater, verkleEntryFactory, storageAccountUpdate);
- }
- if (hasherContext.hasCodeTrieKeys()) {
- final DiffBasedValue codeUpdate =
- worldStateUpdater.getCodeToUpdate().get(accountKey);
- updateCode(accountKey, stateTrie, maybeStateUpdater, verkleEntryFactory, codeUpdate);
- }
- updateTheAccount(
- accountKey, stateTrie, maybeStateUpdater, verkleEntryFactory, accountUpdate.getValue());
+ for (final Address accountKey : addressesToPersist) {
+ updateState(
+ accountKey,
+ stateTrie,
+ maybeStateUpdater,
+ preloadedHashers.get(accountKey),
+ worldStateUpdater);
}
LOG.info("start commit ");
@@ -206,124 +199,72 @@ protected Hash internalCalculateRootHash(
return Hash.wrap(rootHash);
}
- private void updateTheAccount(
+ private static boolean codeIsEmpty(final Bytes value) {
+ return value == null || value.isEmpty();
+ }
+
+ private void generateAccountValues(
final Address accountKey,
- final VerkleTrie stateTrie,
- final Optional maybeStateUpdater,
final VerkleEntryFactory verkleEntryFactory,
+ final Optional maybeStateUpdater,
final DiffBasedValue accountUpdate) {
-
- if (!accountUpdate.isUnchanged()) {
- final VerkleAccount priorAccount = accountUpdate.getPrior();
- final VerkleAccount updatedAccount = accountUpdate.getUpdated();
- if (updatedAccount == null) {
- final Hash addressHash = hashAndSavePreImage(accountKey);
- verkleEntryFactory
- .generateKeysForAccount(accountKey)
- .forEach(
- bytes -> {
- System.out.println(
- "remove "
- + accountKey
- + " "
- + bytes
- + " "
- + accountUpdate.getPrior()
- + " "
- + accountUpdate.getUpdated());
- stateTrie.remove(bytes);
- });
- maybeStateUpdater.ifPresent(
- bonsaiUpdater -> bonsaiUpdater.removeAccountInfoState(addressHash));
- } else {
- final Bytes priorValue = priorAccount == null ? null : priorAccount.serializeAccount();
- final Bytes accountValue = updatedAccount.serializeAccount();
- if (!accountValue.equals(priorValue)) {
- verkleEntryFactory
- .generateKeyValuesForAccount(
- accountKey,
- updatedAccount.getNonce(),
- updatedAccount.getBalance(),
- updatedAccount.getCodeHash())
- .forEach(
- (bytes, bytes2) -> {
- System.out.println(
- "add "
- + accountKey
- + " "
- + bytes
- + " "
- + bytes2
- + " "
- + updatedAccount.getBalance());
- stateTrie.put(bytes, bytes2);
- });
- maybeStateUpdater.ifPresent(
- bonsaiUpdater ->
- bonsaiUpdater.putAccountInfoState(hashAndSavePreImage(accountKey), accountValue));
- }
- }
+ if (accountUpdate == null || accountUpdate.isUnchanged()) {
+ return;
+ }
+ if (accountUpdate.getUpdated() == null) {
+ verkleEntryFactory.generateAccountKeysForRemoval(accountKey);
+ final Hash addressHash = hashAndSavePreImage(accountKey);
+ maybeStateUpdater.ifPresent(
+ bonsaiUpdater -> bonsaiUpdater.removeAccountInfoState(addressHash));
+ return;
}
+ final VerkleAccount updatedAcount = accountUpdate.getUpdated();
+ verkleEntryFactory.generateAccountKeyValueForUpdate(
+ accountKey, updatedAcount.getNonce(), updatedAcount.getBalance());
+ maybeStateUpdater.ifPresent(
+ bonsaiUpdater ->
+ bonsaiUpdater.putAccountInfoState(
+ hashAndSavePreImage(accountKey), updatedAcount.serializeAccount()));
}
- private void updateCode(
+ private void generateCodeValues(
final Address accountKey,
- final VerkleTrie stateTrie,
- final Optional maybeStateUpdater,
final VerkleEntryFactory verkleEntryFactory,
+ final Optional maybeStateUpdater,
final DiffBasedValue codeUpdate) {
- final Bytes priorCode = codeUpdate.getPrior();
- final Bytes updatedCode = codeUpdate.getUpdated();
- final Hash accountHash = accountKey.addressHash();
- if (updatedCode == null) {
- final Hash priorCodeHash = Hash.hash(priorCode);
- verkleEntryFactory
- .generateKeysForCode(accountKey, priorCode)
- .forEach(
- bytes -> {
- System.out.println("remove code " + bytes);
- stateTrie.remove(bytes);
- });
+ if (codeUpdate == null
+ || codeUpdate.isUnchanged()
+ || (codeIsEmpty(codeUpdate.getPrior()) && codeIsEmpty(codeUpdate.getUpdated()))) {
+ return;
+ }
+ if (codeUpdate.getUpdated() == null) {
+ final Hash priorCodeHash = Hash.hash(codeUpdate.getPrior());
+ verkleEntryFactory.generateCodeKeysForRemoval(accountKey, codeUpdate.getPrior());
+ final Hash accountHash = accountKey.addressHash();
maybeStateUpdater.ifPresent(
bonsaiUpdater -> bonsaiUpdater.removeCode(accountHash, priorCodeHash));
+ return;
+ }
+ final Hash accountHash = accountKey.addressHash();
+ final Hash codeHash = Hash.hash(codeUpdate.getUpdated());
+ verkleEntryFactory.generateCodeKeyValuesForUpdate(
+ accountKey, codeUpdate.getUpdated(), codeHash);
+ if (codeUpdate.getUpdated().isEmpty()) {
+ maybeStateUpdater.ifPresent(bonsaiUpdater -> bonsaiUpdater.removeCode(accountHash, codeHash));
} else {
- if (updatedCode.isEmpty()) {
- final Hash codeHash = Hash.hash(updatedCode);
- verkleEntryFactory
- .generateKeyValuesForCode(accountKey, updatedCode)
- .forEach(
- (bytes, bytes2) -> {
- // System.out.println("add code " + bytes + " " + bytes2);
- stateTrie.put(bytes, bytes2);
- });
- maybeStateUpdater.ifPresent(
- bonsaiUpdater -> bonsaiUpdater.removeCode(accountHash, codeHash));
- } else {
- final Hash codeHash = Hash.hash(updatedCode);
- verkleEntryFactory
- .generateKeyValuesForCode(accountKey, updatedCode)
- .forEach(
- (bytes, bytes2) -> {
- System.out.println("add code " + bytes + " " + bytes2);
- stateTrie.put(bytes, bytes2);
- });
- maybeStateUpdater.ifPresent(
- bonsaiUpdater -> bonsaiUpdater.putCode(accountHash, codeHash, updatedCode));
- }
+ maybeStateUpdater.ifPresent(
+ bonsaiUpdater -> bonsaiUpdater.putCode(accountHash, codeHash, codeUpdate.getUpdated()));
}
}
- private boolean codeIsEmpty(final Bytes value) {
- return value == null || value.isEmpty();
- }
-
- private void updateAccountStorageState(
+ private void generateStorageValues(
final Address accountKey,
- final VerkleTrie stateTrie,
- final Optional maybeStateUpdater,
final VerkleEntryFactory verkleEntryFactory,
+ final Optional maybeStateUpdater,
final StorageConsumingMap> storageAccountUpdate) {
-
+ if (storageAccountUpdate == null || storageAccountUpdate.keySet().isEmpty()) {
+ return;
+ }
final Hash updatedAddressHash = accountKey.addressHash();
// for manicured tries and composting, collect branches here (not implemented)
for (final Map.Entry> storageUpdate :
@@ -333,30 +274,13 @@ private void updateAccountStorageState(
if (!storageUpdate.getValue().isUnchanged()) {
final UInt256 updatedStorage = storageUpdate.getValue().getUpdated();
if (updatedStorage == null) {
- verkleEntryFactory
- .generateKeysForStorage(accountKey, storageUpdate.getKey())
- .forEach(
- bytes -> {
- System.out.println("remove storage" + bytes);
- stateTrie.remove(bytes);
- });
+ verkleEntryFactory.generateStorageKeysForRemoval(accountKey, storageUpdate.getKey());
maybeStateUpdater.ifPresent(
diffBasedUpdater ->
diffBasedUpdater.removeStorageValueBySlotHash(updatedAddressHash, slotHash));
} else {
- final Pair storage =
- verkleEntryFactory.generateKeyValuesForStorage(
- accountKey, storageUpdate.getKey(), updatedStorage);
- System.out.println("add storage " + storage.getFirst() + " " + storage.getSecond());
- stateTrie
- .put(storage.getFirst(), storage.getSecond())
- .ifPresentOrElse(
- bytes -> {
- storageUpdate.getValue().setPrior(UInt256.fromBytes(bytes));
- },
- () -> {
- storageUpdate.getValue().setPrior(null);
- });
+ verkleEntryFactory.generateStorageKeyValueForUpdate(
+ accountKey, storageUpdate.getKey(), updatedStorage);
maybeStateUpdater.ifPresent(
bonsaiUpdater ->
bonsaiUpdater.putStorageValueBySlotHash(
@@ -366,6 +290,79 @@ private void updateAccountStorageState(
}
}
+ private void updateState(
+ final Address accountKey,
+ final VerkleTrie stateTrie,
+ final Optional maybeStateUpdater,
+ final HasherContext hasherContext,
+ final VerkleWorldStateUpdateAccumulator worldStateUpdater) {
+
+ final VerkleEntryFactory verkleEntryFactory = new VerkleEntryFactory(hasherContext.hasher());
+
+ generateAccountValues(
+ accountKey,
+ verkleEntryFactory,
+ maybeStateUpdater,
+ worldStateUpdater.getAccountsToUpdate().get(accountKey));
+
+ generateCodeValues(
+ accountKey,
+ verkleEntryFactory,
+ maybeStateUpdater,
+ worldStateUpdater.getCodeToUpdate().get(accountKey));
+
+ generateStorageValues(
+ accountKey,
+ verkleEntryFactory,
+ maybeStateUpdater,
+ worldStateUpdater.getStorageToUpdate().get(accountKey));
+
+ verkleEntryFactory
+ .getKeysForRemoval()
+ .forEach(
+ key -> {
+ System.out.println("remove key " + key);
+ stateTrie.remove(key);
+ });
+ verkleEntryFactory
+ .getNonStorageKeyValuesForUpdate()
+ .forEach(
+ (key, value) -> {
+ System.out.println("add key " + key + " leaf value " + value);
+ stateTrie.put(key, value);
+ });
+ verkleEntryFactory
+ .getStorageKeyValuesForUpdate()
+ .forEach(
+ (storageSlotKey, pair) -> {
+ var storageAccountUpdate = worldStateUpdater.getStorageToUpdate().get(accountKey);
+ if (storageAccountUpdate == null) {
+ return;
+ }
+ System.out.println(
+ "add storage key " + pair.getFirst() + " value " + pair.getSecond());
+ Optional> storageUpdate =
+ Optional.ofNullable(storageAccountUpdate.get(storageSlotKey));
+ stateTrie
+ .put(pair.getFirst(), pair.getSecond())
+ .ifPresentOrElse(
+ bytes ->
+ storageUpdate.ifPresent(
+ storage -> storage.setPrior(UInt256.fromBytes(bytes))),
+ () -> storageUpdate.ifPresent(storage -> storage.setPrior(null)));
+ });
+ }
+
+ public Set getAddressesToPersist(
+ final DiffBasedWorldStateUpdateAccumulator> accumulator) {
+ Set mergedAddresses =
+ new HashSet<>(accumulator.getAccountsToUpdate().keySet()); // accountsToUpdate
+ mergedAddresses.addAll(accumulator.getCodeToUpdate().keySet()); // codeToUpdate
+ mergedAddresses.addAll(accumulator.getStorageToClear()); // storageToClear
+ mergedAddresses.addAll(accumulator.getStorageToUpdate().keySet()); // storageToUpdate
+ return mergedAddresses;
+ }
+
@Override
public MutableWorldState freeze() {
this.worldStateConfig.setFrozen(true);
diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/LogRollingTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/LogRollingTests.java
index a474fe412ff..823552d9c95 100644
--- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/LogRollingTests.java
+++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/LogRollingTests.java
@@ -84,7 +84,7 @@ class LogRollingTests {
Hash.ZERO,
Hash.EMPTY_LIST_HASH,
Address.ZERO,
- Hash.fromHexString("0x47ce198a7fb1089549001a5e0838e9ef3ab6e75f9c97cbb4d6f3243a779b64d2"),
+ Hash.fromHexString("0x4a5e0191f58b8c79ed86ac489d0e54709ae8ea089c491dd1f204212b8fb43abd"),
Hash.EMPTY_TRIE_HASH,
Hash.EMPTY_LIST_HASH,
LogsBloomFilter.builder().build(),
@@ -109,7 +109,7 @@ class LogRollingTests {
headerOne.getHash(),
Hash.EMPTY_LIST_HASH,
Address.ZERO,
- Hash.fromHexString("0x48bb5935338f43503c7c2452059dee413fcda1716ae64c65a46d4c54ee8ddbb8"),
+ Hash.fromHexString("0x2499426e9eae43d5fdefe22a184f6f3de51e6c0ea1b41a2aab53a031e99fb49e"),
Hash.EMPTY_TRIE_HASH,
Hash.EMPTY_LIST_HASH,
LogsBloomFilter.builder().build(),
@@ -134,7 +134,7 @@ class LogRollingTests {
headerOne.getHash(),
Hash.EMPTY_LIST_HASH,
Address.ZERO,
- Hash.fromHexString("0x147f9bf4e32b84b49a2e6debe6831059148b0b818d9ff5ff7e3634676c285490"),
+ Hash.fromHexString("0x4186e2e00886884e95dcdd7ef1803e2fdab3918e89d811e56f52e2f077b804ec"),
Hash.EMPTY_TRIE_HASH,
Hash.EMPTY_LIST_HASH,
LogsBloomFilter.builder().build(),
@@ -160,7 +160,7 @@ class LogRollingTests {
headerOne.getHash(),
Hash.EMPTY_LIST_HASH,
Address.ZERO,
- Hash.fromHexString("0x47ce198a7fb1089549001a5e0838e9ef3ab6e75f9c97cbb4d6f3243a779b64d2"),
+ Hash.fromHexString("0x4a5e0191f58b8c79ed86ac489d0e54709ae8ea089c491dd1f204212b8fb43abd"),
Hash.EMPTY_TRIE_HASH,
Hash.EMPTY_LIST_HASH,
LogsBloomFilter.builder().build(),
diff --git a/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/TrieKeyPreloader.java b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/TrieKeyPreloader.java
index 2bdf4961ed9..d7bf2e92acb 100644
--- a/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/TrieKeyPreloader.java
+++ b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/TrieKeyPreloader.java
@@ -22,9 +22,9 @@
import org.hyperledger.besu.ethereum.trie.verkle.hasher.PedersenHasher;
import org.hyperledger.besu.ethereum.trie.verkle.util.Parameters;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.tuweni.bytes.Bytes;
@@ -40,22 +40,16 @@ public class TrieKeyPreloader {
public TrieKeyPreloader() {
this.hasher = new PedersenHasher();
trieKeyAdapter = new TrieKeyBatchAdapter(hasher);
- trieKeyAdapter.versionKey(
- Address.ZERO); // TODO REMOVE is just to preload the native library for performance check
}
- public List generateAccountKeyIds() {
- final List keys = new ArrayList<>();
- keys.add(Parameters.VERSION_LEAF_KEY);
- keys.add(Parameters.BALANCE_LEAF_KEY);
- keys.add(Parameters.NONCE_LEAF_KEY);
- keys.add(Parameters.CODE_KECCAK_LEAF_KEY);
- return keys;
+ public Bytes32 generateAccountKeyId() {
+ return Parameters.BASIC_DATA_LEAF_KEY;
}
public List generateCodeChunkKeyIds(final Bytes code) {
- return new ArrayList<>(
- IntStream.range(0, trieKeyAdapter.getNbChunk(code)).mapToObj(UInt256::valueOf).toList());
+ return IntStream.range(0, trieKeyAdapter.getNbChunk(code))
+ .mapToObj(UInt256::valueOf)
+ .collect(Collectors.toUnmodifiableList());
}
public List generateStorageKeyIds(final Set storageSlotKeys) {
diff --git a/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/VerkleEntryFactory.java b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/VerkleEntryFactory.java
index 97babec0215..98a391ffdaf 100644
--- a/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/VerkleEntryFactory.java
+++ b/ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/VerkleEntryFactory.java
@@ -20,11 +20,13 @@
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.trie.verkle.adapter.TrieKeyBatchAdapter;
import org.hyperledger.besu.ethereum.trie.verkle.hasher.Hasher;
+import org.hyperledger.besu.ethereum.trie.verkle.util.SuffixTreeEncoder;
-import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import kotlin.Pair;
import org.apache.tuweni.bytes.Bytes;
@@ -34,64 +36,89 @@
public class VerkleEntryFactory {
private final TrieKeyBatchAdapter trieKeyAdapter;
+ private final HashSet keysForRemoval = new HashSet<>();
+ private final HashMap nonStorageKeyValuesForUpdate = new HashMap<>();
+ private final HashMap> storageKeyValuesForUpdate =
+ new HashMap<>();
public VerkleEntryFactory(final Hasher hasher) {
trieKeyAdapter = new TrieKeyBatchAdapter(hasher);
}
- public Map generateKeyValuesForAccount(
- final Address address, final long nonce, final Wei balance, final Hash codeHash) {
- final Map keyValues = new HashMap<>();
- keyValues.put(trieKeyAdapter.versionKey(address), Bytes32.ZERO);
- keyValues.put(trieKeyAdapter.balanceKey(address), toLittleEndian(balance));
- keyValues.put(trieKeyAdapter.nonceKey(address), toLittleEndian(UInt256.valueOf(nonce)));
- keyValues.put(trieKeyAdapter.codeKeccakKey(address), codeHash);
- return keyValues;
+ public void generateAccountKeysForRemoval(final Address address) {
+ keysForRemoval.add(trieKeyAdapter.basicDataKey(address));
}
- public List generateKeysForAccount(final Address address) {
- final List keys = new ArrayList<>();
- keys.add(trieKeyAdapter.versionKey(address));
- keys.add(trieKeyAdapter.balanceKey(address));
- keys.add(trieKeyAdapter.nonceKey(address));
- keys.add(trieKeyAdapter.codeKeccakKey(address));
- return keys;
- }
-
- public Map generateKeyValuesForCode(final Address address, final Bytes code) {
- final Map keyValues = new HashMap<>();
- keyValues.put(
- trieKeyAdapter.codeSizeKey(address), toLittleEndian(UInt256.valueOf(code.size())));
+ public void generateCodeKeysForRemoval(final Address address, final Bytes code) {
+ keysForRemoval.add(trieKeyAdapter.basicDataKey(address));
+ keysForRemoval.add(trieKeyAdapter.codeHashKey(address));
List codeChunks = trieKeyAdapter.chunkifyCode(code);
for (int i = 0; i < codeChunks.size(); i++) {
- keyValues.put(trieKeyAdapter.codeChunkKey(address, UInt256.valueOf(i)), codeChunks.get(i));
+ keysForRemoval.add(trieKeyAdapter.codeChunkKey(address, UInt256.valueOf(i)));
+ }
+ }
+
+ public void generateStorageKeysForRemoval(
+ final Address address, final StorageSlotKey storageKey) {
+ keysForRemoval.add(trieKeyAdapter.storageKey(address, storageKey.getSlotKey().orElseThrow()));
+ }
+
+ public void generateAccountKeyValueForUpdate(
+ final Address address, final long nonce, final Wei balance) {
+ Bytes32 basicDataKey = trieKeyAdapter.basicDataKey(address);
+ Bytes32 basicDataValue;
+ if ((basicDataValue = nonStorageKeyValuesForUpdate.get(basicDataKey)) == null) {
+ basicDataValue = Bytes32.ZERO;
+ } else {
+ basicDataValue = SuffixTreeEncoder.eraseVersion(basicDataValue);
+ basicDataValue = SuffixTreeEncoder.eraseNonce(basicDataValue);
+ basicDataValue = SuffixTreeEncoder.eraseBalance(basicDataValue);
}
- return keyValues;
+
+ basicDataValue = SuffixTreeEncoder.addVersionIntoValue(basicDataValue, Bytes32.ZERO);
+ basicDataValue = SuffixTreeEncoder.addNonceIntoValue(basicDataValue, UInt256.valueOf(nonce));
+ basicDataValue = SuffixTreeEncoder.addBalanceIntoValue(basicDataValue, balance);
+ nonStorageKeyValuesForUpdate.put(basicDataKey, basicDataValue);
}
- public List generateKeysForCode(final Address address, final Bytes code) {
- final List keys = new ArrayList<>();
- keys.add(trieKeyAdapter.codeKeccakKey(address));
- keys.add(trieKeyAdapter.codeSizeKey(address));
+ public void generateCodeKeyValuesForUpdate(
+ final Address address, final Bytes code, final Hash codeHash) {
+ Bytes32 basicDataKey = trieKeyAdapter.basicDataKey(address);
+ Bytes32 basicDataValue;
+ if ((basicDataValue = nonStorageKeyValuesForUpdate.get(basicDataKey)) == null) {
+ basicDataValue = Bytes32.ZERO;
+ } else {
+ basicDataValue = SuffixTreeEncoder.eraseCodeSize(basicDataValue);
+ }
+
+ basicDataValue =
+ SuffixTreeEncoder.addCodeSizeIntoValue(basicDataValue, UInt256.valueOf(code.size()));
+ nonStorageKeyValuesForUpdate.put(basicDataKey, basicDataValue);
+ nonStorageKeyValuesForUpdate.put(trieKeyAdapter.codeHashKey(address), codeHash);
List codeChunks = trieKeyAdapter.chunkifyCode(code);
for (int i = 0; i < codeChunks.size(); i++) {
- keys.add(trieKeyAdapter.codeChunkKey(address, UInt256.valueOf(i)));
+ nonStorageKeyValuesForUpdate.put(
+ trieKeyAdapter.codeChunkKey(address, UInt256.valueOf(i)), codeChunks.get(i));
}
- return keys;
}
- public Pair generateKeyValuesForStorage(
- final Address address, final StorageSlotKey storageKey, final Bytes value) {
- return new Pair<>(
- trieKeyAdapter.storageKey(address, storageKey.getSlotKey().orElseThrow()), value);
+ public void generateStorageKeyValueForUpdate(
+ final Address address, final StorageSlotKey storageSlotKey, final Bytes32 value) {
+ storageKeyValuesForUpdate.put(
+ storageSlotKey,
+ new Pair<>(
+ trieKeyAdapter.storageKey(address, storageSlotKey.getSlotKey().orElseThrow()), value));
}
- public List generateKeysForStorage(
- final Address address, final StorageSlotKey storageKey) {
- return List.of(trieKeyAdapter.storageKey(address, storageKey.getSlotKey().orElseThrow()));
+ public Set getKeysForRemoval() {
+ return keysForRemoval;
+ }
+
+ public Map getNonStorageKeyValuesForUpdate() {
+ return nonStorageKeyValuesForUpdate;
}
- private static Bytes toLittleEndian(final Bytes originalValue) {
- return originalValue.reverse();
+ public Map> getStorageKeyValuesForUpdate() {
+ return storageKeyValuesForUpdate;
}
}
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java
index 8a175f917c6..5378f1623f3 100644
--- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java
+++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java
@@ -15,17 +15,13 @@
package org.hyperledger.besu.evm.gascalculator;
import static org.hyperledger.besu.datatypes.Address.KZG_POINT_EVAL;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.BALANCE_LEAF_KEY;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_KECCAK_LEAF_KEY;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_SIZE_LEAF_KEY;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.NONCE_LEAF_KEY;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.VERSION_LEAF_KEY;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import org.hyperledger.besu.datatypes.AccessWitness;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.Wei;
+import org.hyperledger.besu.ethereum.trie.verkle.util.Parameters;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
@@ -179,13 +175,7 @@ public long extCodeCopyOperationGasCost(
long statelessGas =
frame
.getAccessWitness()
- .touchAddressOnReadAndComputeGas(address, UInt256.ZERO, VERSION_LEAF_KEY);
- statelessGas =
- clampedAdd(
- statelessGas,
- frame
- .getAccessWitness()
- .touchAddressOnReadAndComputeGas(address, UInt256.ZERO, CODE_SIZE_LEAF_KEY));
+ .touchAddressOnReadAndComputeGas(address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (statelessGas == 0) {
statelessGas = getWarmStorageReadCost();
}
@@ -254,7 +244,8 @@ public long getBalanceOperationGasCost(
final long statelessGas =
frame
.getAccessWitness()
- .touchAddressOnReadAndComputeGas(address, UInt256.ZERO, BALANCE_LEAF_KEY);
+ .touchAddressOnReadAndComputeGas(
+ address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (statelessGas == 0) {
return getWarmStorageReadCost();
} else {
@@ -275,7 +266,8 @@ public long extCodeHashOperationGasCost(
final long statelessGas =
frame
.getAccessWitness()
- .touchAddressOnReadAndComputeGas(address, UInt256.ZERO, CODE_KECCAK_LEAF_KEY);
+ .touchAddressOnReadAndComputeGas(
+ address, UInt256.ZERO, Parameters.CODE_HASH_LEAF_KEY);
if (statelessGas == 0) {
return getWarmStorageReadCost();
} else {
@@ -298,13 +290,8 @@ public long getExtCodeSizeOperationGasCost(
long statelessGas =
frame
.getAccessWitness()
- .touchAddressOnReadAndComputeGas(address, UInt256.ZERO, VERSION_LEAF_KEY);
- statelessGas =
- clampedAdd(
- statelessGas,
- frame
- .getAccessWitness()
- .touchAddressOnReadAndComputeGas(address, UInt256.ZERO, CODE_SIZE_LEAF_KEY));
+ .touchAddressOnReadAndComputeGas(
+ address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (statelessGas == 0) {
return getWarmStorageReadCost();
} else {
@@ -331,7 +318,8 @@ public long selfDestructOperationGasCost(
long statelessGas =
frame
.getAccessWitness()
- .touchAddressOnReadAndComputeGas(originatorAddress, UInt256.ZERO, BALANCE_LEAF_KEY);
+ .touchAddressOnReadAndComputeGas(
+ originatorAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (!originatorAddress.equals(recipientAddress)) {
statelessGas =
clampedAdd(
@@ -339,7 +327,7 @@ public long selfDestructOperationGasCost(
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
- recipientAddress, UInt256.ZERO, BALANCE_LEAF_KEY));
+ recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
}
if (!inheritance.isZero()) {
statelessGas =
@@ -348,7 +336,7 @@ public long selfDestructOperationGasCost(
frame
.getAccessWitness()
.touchAddressOnWriteAndComputeGas(
- originatorAddress, UInt256.ZERO, BALANCE_LEAF_KEY));
+ originatorAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
if (!originatorAddress.equals(recipientAddress)) {
statelessGas =
clampedAdd(
@@ -356,7 +344,7 @@ public long selfDestructOperationGasCost(
frame
.getAccessWitness()
.touchAddressOnWriteAndComputeGas(
- recipientAddress, UInt256.ZERO, BALANCE_LEAF_KEY));
+ recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
}
if (recipient == null) {
statelessGas =
@@ -365,14 +353,7 @@ public long selfDestructOperationGasCost(
frame
.getAccessWitness()
.touchAddressOnWriteAndComputeGas(
- recipientAddress, UInt256.ZERO, VERSION_LEAF_KEY));
- statelessGas =
- clampedAdd(
- statelessGas,
- frame
- .getAccessWitness()
- .touchAddressOnWriteAndComputeGas(
- recipientAddress, UInt256.ZERO, NONCE_LEAF_KEY));
+ recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
}
}
return clampedAdd(gasCost, statelessGas);
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java
index 0001654862e..390c429b670 100644
--- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java
+++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java
@@ -14,13 +14,10 @@
*/
package org.hyperledger.besu.evm.gascalculator.stateless;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.BALANCE_LEAF_KEY;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_KECCAK_LEAF_KEY;
+import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.BASIC_DATA_LEAF_KEY;
+import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_HASH_LEAF_KEY;
import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_OFFSET;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_SIZE_LEAF_KEY;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.NONCE_LEAF_KEY;
import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.VERKLE_NODE_WIDTH;
-import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.VERSION_LEAF_KEY;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import org.hyperledger.besu.datatypes.Address;
@@ -69,32 +66,18 @@ public List keys() {
@Override
public long touchAndChargeProofOfAbsence(final Address address) {
long gas = 0;
- gas =
- clampedAdd(gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, VERSION_LEAF_KEY));
- gas =
- clampedAdd(gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BALANCE_LEAF_KEY));
- gas = clampedAdd(gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, NONCE_LEAF_KEY));
gas =
clampedAdd(
- gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_KECCAK_LEAF_KEY));
+ gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
- gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_SIZE_LEAF_KEY));
+ gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas;
}
@Override
public long touchAndChargeMessageCall(final Address address) {
-
- long gas = 0;
-
- gas =
- clampedAdd(gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, VERSION_LEAF_KEY));
- gas =
- clampedAdd(
- gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_SIZE_LEAF_KEY));
-
- return gas;
+ return touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
}
@Override
@@ -103,9 +86,11 @@ public long touchAndChargeValueTransfer(final Address caller, final Address targ
long gas = 0;
gas =
- clampedAdd(gas, touchAddressOnWriteAndComputeGas(caller, zeroTreeIndex, BALANCE_LEAF_KEY));
+ clampedAdd(
+ gas, touchAddressOnWriteAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
- clampedAdd(gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BALANCE_LEAF_KEY));
+ clampedAdd(
+ gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
return gas;
}
@@ -117,13 +102,13 @@ public long touchAndChargeContractCreateInit(
long gas = 0;
gas =
- clampedAdd(gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, VERSION_LEAF_KEY));
- gas = clampedAdd(gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, NONCE_LEAF_KEY));
+ clampedAdd(
+ gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
if (createSendsValue) {
gas =
clampedAdd(
- gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BALANCE_LEAF_KEY));
+ gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
}
return gas;
}
@@ -133,17 +118,12 @@ public long touchAndChargeContractCreateCompleted(final Address address) {
long gas = 0;
- gas =
- clampedAdd(gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, VERSION_LEAF_KEY));
- gas =
- clampedAdd(gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BALANCE_LEAF_KEY));
- gas = clampedAdd(gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, NONCE_LEAF_KEY));
gas =
clampedAdd(
- gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, CODE_KECCAK_LEAF_KEY));
+ gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
- gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, CODE_SIZE_LEAF_KEY));
+ gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas;
}
@@ -154,15 +134,14 @@ public long touchTxOriginAndComputeGas(final Address origin) {
long gas = 0;
- gas = clampedAdd(gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, VERSION_LEAF_KEY));
gas =
- clampedAdd(gas, touchAddressOnWriteAndComputeGas(origin, zeroTreeIndex, BALANCE_LEAF_KEY));
- gas = clampedAdd(gas, touchAddressOnWriteAndComputeGas(origin, zeroTreeIndex, NONCE_LEAF_KEY));
+ clampedAdd(
+ gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
- gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_KECCAK_LEAF_KEY));
+ gas, touchAddressOnWriteAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
- clampedAdd(gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_SIZE_LEAF_KEY));
+ clampedAdd(gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_HASH_LEAF_KEY));
// modifying this after update on EIP-4762 to not charge simple transfers
@@ -175,21 +154,16 @@ public long touchTxExistingAndComputeGas(final Address target, final boolean sen
long gas = 0;
- gas = clampedAdd(gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, VERSION_LEAF_KEY));
- gas = clampedAdd(gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, NONCE_LEAF_KEY));
- gas =
- clampedAdd(gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, CODE_SIZE_LEAF_KEY));
gas =
clampedAdd(
- gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, CODE_KECCAK_LEAF_KEY));
+ gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
+ gas =
+ clampedAdd(gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY));
if (sendsValue) {
gas =
clampedAdd(
- gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BALANCE_LEAF_KEY));
- } else {
- gas =
- clampedAdd(gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BALANCE_LEAF_KEY));
+ gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
}
// modifying this after update on EIP-4762 to not charge simple transfers
diff --git a/gradle/versions.gradle b/gradle/versions.gradle
index 099a5afc83a..fbe2ebf69ca 100644
--- a/gradle/versions.gradle
+++ b/gradle/versions.gradle
@@ -169,7 +169,7 @@ dependencyManagement {
entry 'ipa-multipoint'
}
- dependency 'org.hyperledger.besu:besu-verkle-trie:0.0.1-SNAPSHOT'
+ dependency 'org.hyperledger.besu:besu-verkle-trie:0.0.3-20240917.164246-1'
dependencySet(group: 'org.immutables', version: '2.10.0') {
entry 'value-annotations'