Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make access event keys eip 6800 compatible #7594

Merged
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.addAll(trieKeyPreloader.generateAccountKeyIds());
}

// 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,75 @@ 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
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

} else 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
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;
} else 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 generateStorageValue(
Copy link
Contributor

Choose a reason for hiding this comment

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

generateStorageValue-S ?

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 +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<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 +290,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));

generateStorageValue(
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
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
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
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
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
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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(),
Expand All @@ -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(),
Expand All @@ -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(),
Expand Down
Loading
Loading