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'