Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

public class VerkleAccount extends DiffBasedAccount {
private Hash storageRoot; // TODO REMOVE AS USELESS
private int hashCode;
Comment on lines 39 to +40

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we remove storageRoot while we are here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good shout. @matkt mentioned he will remove it in future PR so I didn't want to create conflicts unnecessarily


public VerkleAccount(
final DiffBasedWorldView context,
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,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;
Expand Down Expand Up @@ -125,8 +124,10 @@ protected Hash internalCalculateRootHash(
accountUpdate -> {
final Address accountKey = accountUpdate.getKey();
// generate account triekeys
final List<Bytes32> accountKeyIds =
new ArrayList<>(trieKeyPreloader.generateAccountKeyIds());
final List<Bytes32> accountKeyIds = new ArrayList<>();
if (!accountUpdate.getValue().isUnchanged()) {
accountKeyIds.add(trieKeyPreloader.generateAccountKeyId());
}

// generate storage triekeys
final List<Bytes32> storageKeyIds = new ArrayList<>();
Expand All @@ -153,7 +154,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));
Expand All @@ -166,24 +167,13 @@ protected Hash internalCalculateRootHash(
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 : worldStateUpdater.getAccountsToUpdate().keySet()) {
updateState(
accountKey,
stateTrie,
maybeStateUpdater,
preloadedHashers.get(accountKey),
worldStateUpdater);
}

LOG.info("start commit ");
Expand All @@ -206,124 +196,77 @@ 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<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.isUnchanged()) {
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we cannot use if, else if , else if ? I think it will be better

}
if (accountUpdate.getUpdated() == null) {
verkleEntryFactory.generateAccountKeysForRemoval(accountKey);
final Hash addressHash = hashAndSavePreImage(accountKey);
maybeStateUpdater.ifPresent(
bonsaiUpdater -> bonsaiUpdater.removeAccountInfoState(addressHash));
return;
}
final Bytes priorValue =
accountUpdate.getPrior() == null ? null : accountUpdate.getPrior().serializeAccount();
final Bytes accountValue = accountUpdate.getUpdated().serializeAccount();
if (!accountValue.equals(priorValue)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verkleEntryFactory.generateAccountKeyValueForUpdate(
accountKey,
accountUpdate.getUpdated().getNonce(),
accountUpdate.getUpdated().getBalance());
maybeStateUpdater.ifPresent(
bonsaiUpdater ->
bonsaiUpdater.putAccountInfoState(hashAndSavePreImage(accountKey), accountValue));
}
}

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 :
Expand All @@ -333,30 +276,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<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(
Expand All @@ -366,6 +292,69 @@ private void updateAccountStorageState(
}
}

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this one is still needed as we already checked here https://github.com/hyperledger/besu/pull/7594/files#diff-a8c0068954d44abe33b85a9a6c2b70309adb959255e417350abe54c858574b62R267 but maybe I missing something ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand. So the link you posted is when the key values are generated. Here we commit them to the trie in one go.
On generation we only have the storageSlotKey and corresponding key/value. We don't have the storageUpdate ref where we should save the prior value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was talking about this check (==null)
https://github.com/hyperledger/besu/pull/7594/files/2d12b659a7478f155f9a7be52e1dfed6dfcb3c7d#diff-a8c0068954d44abe33b85a9a6c2b70309adb959255e417350abe54c858574b62R341

when we are generating the key we already check if the collection is empty, so imo when we enter this loop we cannot have a null

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you're right that we if there are elements we can't have null. I added it based on a defensive approach since we don't know how the programmer will generate keys and values. What if it's based on another object?
If someone changes the code above, then we will not break here. I would prefer to leave it

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)));
});
}

@Override
public MutableWorldState freeze() {
this.worldStateConfig.setFrozen(true);
Expand Down
Loading