Make access event keys eip 6800 compatible (#7594)

This commit encodes balance, nonce, version and code size into a single leaf in Verkle, while code hash has its own leaf, as mandated by EIP-6800.

Signed-off-by: Luis Pinto <luis.pinto@consensys.net>
Co-authored-by: Karim Taam <karim.t2am@gmail.com>
pull/7681/head
Luis Pinto 2 months ago committed by GitHub
parent 7626f2af6b
commit d64829e602
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 30
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/VerkleAccount.java
  2. 287
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java
  3. 8
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/LogRollingTests.java
  4. 18
      ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/TrieKeyPreloader.java
  5. 105
      ethereum/verkletrie/src/main/java/org/hyperledger/besu/ethereum/verkletrie/VerkleEntryFactory.java
  6. 47
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java
  7. 70
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java
  8. 2
      gradle/versions.gradle

@ -37,6 +37,7 @@ import org.apache.tuweni.bytes.Bytes32;
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 class VerkleAccount extends DiffBasedAccount {
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);
}
}

@ -44,6 +44,7 @@ import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier;
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.Set;
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 @@ public class VerkleWorldState extends DiffBasedWorldState {
final Map<Address, HasherContext> preloadedHashers = new ConcurrentHashMap<>();
worldStateUpdater.getAccountsToUpdate().entrySet().parallelStream()
final Set<Address> addressesToPersist = getAddressesToPersist(worldStateUpdater);
addressesToPersist.parallelStream()
.forEach(
accountUpdate -> {
final Address accountKey = accountUpdate.getKey();
accountKey -> {
final DiffBasedValue<VerkleAccount> accountUpdate =
worldStateUpdater.getAccountsToUpdate().get(accountKey);
// generate account triekeys
final List<Bytes32> accountKeyIds =
new ArrayList<>(trieKeyPreloader.generateAccountKeyIds());
final List<Bytes32> accountKeyIds = new ArrayList<>();
if (accountUpdate != null && !accountUpdate.isUnchanged()) {
accountKeyIds.add(trieKeyPreloader.generateAccountKeyId());
}
// generate storage triekeys
final List<Bytes32> storageKeyIds = new ArrayList<>();
@ -153,7 +157,7 @@ public class VerkleWorldState extends DiffBasedWorldState {
!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 @@ public class VerkleWorldState extends DiffBasedWorldState {
accountKey, accountKeyIds, storageKeyIds, codeKeyIds));
});
for (final Map.Entry<Address, DiffBasedValue<VerkleAccount>> 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<StorageSlotKey, DiffBasedValue<UInt256>> storageAccountUpdate =
worldStateUpdater.getStorageToUpdate().get(accountKey);
updateAccountStorageState(
accountKey, stateTrie, maybeStateUpdater, verkleEntryFactory, storageAccountUpdate);
}
if (hasherContext.hasCodeTrieKeys()) {
final DiffBasedValue<Bytes> 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 @@ public class VerkleWorldState extends DiffBasedWorldState {
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<VerkleWorldStateKeyValueStorage.Updater> maybeStateUpdater,
final VerkleEntryFactory verkleEntryFactory,
final Optional<VerkleWorldStateKeyValueStorage.Updater> maybeStateUpdater,
final DiffBasedValue<VerkleAccount> 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<VerkleWorldStateKeyValueStorage.Updater> maybeStateUpdater,
final VerkleEntryFactory verkleEntryFactory,
final Optional<VerkleWorldStateKeyValueStorage.Updater> maybeStateUpdater,
final DiffBasedValue<Bytes> 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<VerkleWorldStateKeyValueStorage.Updater> maybeStateUpdater,
final VerkleEntryFactory verkleEntryFactory,
final Optional<VerkleWorldStateKeyValueStorage.Updater> maybeStateUpdater,
final StorageConsumingMap<StorageSlotKey, DiffBasedValue<UInt256>> 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<StorageSlotKey, DiffBasedValue<UInt256>> storageUpdate :
@ -333,30 +274,13 @@ public class VerkleWorldState extends DiffBasedWorldState {
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<Bytes, Bytes> 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 @@ public class VerkleWorldState extends DiffBasedWorldState {
}
}
private void updateState(
final Address accountKey,
final VerkleTrie stateTrie,
final Optional<VerkleWorldStateKeyValueStorage.Updater> 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<DiffBasedValue<UInt256>> 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<Address> getAddressesToPersist(
final DiffBasedWorldStateUpdateAccumulator<?> accumulator) {
Set<Address> 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);

@ -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(),

@ -22,9 +22,9 @@ import org.hyperledger.besu.ethereum.trie.verkle.hasher.Hasher;
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<Bytes32> generateAccountKeyIds() {
final List<Bytes32> 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<Bytes32> 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<Bytes32> generateStorageKeyIds(final Set<StorageSlotKey> storageSlotKeys) {

@ -20,11 +20,13 @@ import org.hyperledger.besu.datatypes.StorageSlotKey;
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 @@ import org.apache.tuweni.units.bigints.UInt256;
public class VerkleEntryFactory {
private final TrieKeyBatchAdapter trieKeyAdapter;
private final HashSet<Bytes32> keysForRemoval = new HashSet<>();
private final HashMap<Bytes32, Bytes32> nonStorageKeyValuesForUpdate = new HashMap<>();
private final HashMap<StorageSlotKey, Pair<Bytes32, Bytes32>> storageKeyValuesForUpdate =
new HashMap<>();
public VerkleEntryFactory(final Hasher hasher) {
trieKeyAdapter = new TrieKeyBatchAdapter(hasher);
}
public Map<Bytes, Bytes> generateKeyValuesForAccount(
final Address address, final long nonce, final Wei balance, final Hash codeHash) {
final Map<Bytes, Bytes> 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<Bytes> generateKeysForAccount(final Address address) {
final List<Bytes> 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<Bytes, Bytes> generateKeyValuesForCode(final Address address, final Bytes code) {
final Map<Bytes, Bytes> 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<UInt256> 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<Bytes> generateKeysForCode(final Address address, final Bytes code) {
final List<Bytes> 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<UInt256> 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<Bytes, Bytes> 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<Bytes> generateKeysForStorage(
final Address address, final StorageSlotKey storageKey) {
return List.of(trieKeyAdapter.storageKey(address, storageKey.getSlotKey().orElseThrow()));
public Set<Bytes32> getKeysForRemoval() {
return keysForRemoval;
}
public Map<Bytes32, Bytes32> getNonStorageKeyValuesForUpdate() {
return nonStorageKeyValuesForUpdate;
}
private static Bytes toLittleEndian(final Bytes originalValue) {
return originalValue.reverse();
public Map<StorageSlotKey, Pair<Bytes32, Bytes32>> getStorageKeyValuesForUpdate() {
return storageKeyValuesForUpdate;
}
}

@ -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 class Eip4762GasCalculator extends PragueGasCalculator {
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 class Eip4762GasCalculator extends PragueGasCalculator {
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 class Eip4762GasCalculator extends PragueGasCalculator {
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 class Eip4762GasCalculator extends PragueGasCalculator {
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 class Eip4762GasCalculator extends PragueGasCalculator {
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 class Eip4762GasCalculator extends PragueGasCalculator {
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 class Eip4762GasCalculator extends PragueGasCalculator {
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 class Eip4762GasCalculator extends PragueGasCalculator {
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 class Eip4762GasCalculator extends PragueGasCalculator {
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);

@ -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 class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
@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 class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
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 class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
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 class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
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 class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
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 class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
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

@ -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'

Loading…
Cancel
Save