Skip to content

Commit

Permalink
Prepare for EIP-4762: make access event keys in line with eip-6800
Browse files Browse the repository at this point in the history
  • Loading branch information
lu-pinto committed Sep 10, 2024
1 parent 7626f2a commit a12ee48
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.hyperledger.besu.ethereum.verkletrie.TrieKeyPreloader.HasherContext;
import org.hyperledger.besu.ethereum.verkletrie.VerkleEntryFactory;
import org.hyperledger.besu.ethereum.verkletrie.VerkleTrie;
import org.hyperledger.besu.ethereum.verkletrie.util.SuffixTreeEncoder;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier;
Expand All @@ -61,6 +62,11 @@
@SuppressWarnings({"unused", "MismatchedQueryAndUpdateOfCollection", "ModifiedButNotUsed"})
public class VerkleWorldState extends DiffBasedWorldState {

private static final Bytes32 VERSION_KEY_MASK = SuffixTreeEncoder.encodeVersion(Bytes.repeat((byte) 0xff, 1)).not();
private static final Bytes32 CODE_SIZE_KEY_MASK = SuffixTreeEncoder.encodeCodeSize(Bytes.repeat((byte) 0xff, 5)).not();
private static final Bytes32 NONCE_KEY_MASK = SuffixTreeEncoder.encodeNonce(Bytes.repeat((byte) 0xff, 8)).not();
private static final Bytes32 BALANCE_KEY_MASK = SuffixTreeEncoder.encodeBalance(Bytes.repeat((byte) 0xff, 16)).not();

private static final Logger LOG = LoggerFactory.getLogger(VerkleWorldState.class);

private final TrieKeyPreloader trieKeyPreloader;
Expand Down Expand Up @@ -153,7 +159,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 Down Expand Up @@ -221,17 +227,23 @@ private void updateTheAccount(
verkleEntryFactory
.generateKeysForAccount(accountKey)
.forEach(
bytes -> {
key -> {
System.out.println(
"remove "
+ accountKey
+ " "
+ bytes
+ " "
+ accountUpdate.getPrior()
+ " "
+ accountUpdate.getUpdated());
stateTrie.remove(bytes);
"remove "
+ accountKey
+ " "
+ key
+ " "
+ accountUpdate.getPrior()
+ " "
+ accountUpdate.getUpdated());
if (key.equals(Parameters.BASIC_DATA_LEAF_KEY)) {
stateTrie.get(key).ifPresent(currentValue -> {
Bytes deletionMask = VERSION_KEY_MASK.and(BALANCE_KEY_MASK).and(NONCE_KEY_MASK);
Bytes encodedBasicDataLeaf = currentValue.and(deletionMask);
stateTrie.put(key, encodedBasicDataLeaf);
});
}
});
maybeStateUpdater.ifPresent(
bonsaiUpdater -> bonsaiUpdater.removeAccountInfoState(addressHash));
Expand All @@ -243,20 +255,27 @@ private void updateTheAccount(
.generateKeyValuesForAccount(
accountKey,
updatedAccount.getNonce(),
updatedAccount.getBalance(),
updatedAccount.getCodeHash())
updatedAccount.getBalance())
.forEach(
(bytes, bytes2) -> {
(key, value) -> {
System.out.println(
"add "
"add key "
+ accountKey
+ " "
+ bytes
+ " "
+ bytes2
+ " "
+ updatedAccount.getBalance());
stateTrie.put(bytes, bytes2);
+ " with value "
+ value
+ " balance "
+ updatedAccount.getBalance()
+ " nonce "
+ updatedAccount.getNonce());
if (key.equals(Parameters.BASIC_DATA_LEAF_KEY)) {
stateTrie.get(key)
.ifPresentOrElse(
currentValue -> {
Bytes deletionMask = VERSION_KEY_MASK.and(BALANCE_KEY_MASK).and(NONCE_KEY_MASK);
stateTrie.put(key, currentValue.and(deletionMask).or(value));
},
() -> stateTrie.put(key, value));
}
});
maybeStateUpdater.ifPresent(
bonsaiUpdater ->
Expand All @@ -280,40 +299,43 @@ private void updateCode(
verkleEntryFactory
.generateKeysForCode(accountKey, priorCode)
.forEach(
bytes -> {
System.out.println("remove code " + bytes);
stateTrie.remove(bytes);
key -> {
System.out.println("remove code from key: " + key);
if (key.equals(Parameters.BASIC_DATA_LEAF_KEY)) {
stateTrie.get(key).ifPresent(currentValue -> stateTrie.put(key, currentValue.and(CODE_SIZE_KEY_MASK)));
} else if (key.equals(Parameters.CODE_HASH_LEAF_KEY)) {
stateTrie.remove(key);
}
});
maybeStateUpdater.ifPresent(
bonsaiUpdater -> bonsaiUpdater.removeCode(accountHash, priorCodeHash));
} else {
final Hash codeHash = Hash.hash(updatedCode);
verkleEntryFactory
.generateKeyValuesForCode(accountKey, updatedCode, codeHash)
.forEach(
(key, value) -> {
System.out.println("add code from key: " + key + " leaf value: " + value);
if (key.equals(Parameters.BASIC_DATA_LEAF_KEY)) {
stateTrie.get(key)
.ifPresentOrElse(
currentValue -> stateTrie.put(key, currentValue.and(CODE_SIZE_KEY_MASK).or(value)),
() -> stateTrie.put(key, value));
} else if (key.equals(Parameters.CODE_HASH_LEAF_KEY)) {
stateTrie.put(key, value);
}
});
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));
return;
}
maybeStateUpdater.ifPresent(
bonsaiUpdater -> bonsaiUpdater.putCode(accountHash, codeHash, updatedCode));
}
}

private boolean codeIsEmpty(final Bytes value) {
private static boolean codeIsEmpty(final Bytes value) {
return value == null || value.isEmpty();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -40,22 +40,14 @@ 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;
return List.of(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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
import org.hyperledger.besu.ethereum.verkletrie.util.SuffixTreeEncoder;

public class VerkleEntryFactory {

Expand All @@ -40,28 +41,26 @@ public VerkleEntryFactory(final Hasher hasher) {
}

public Map<Bytes, Bytes> generateKeyValuesForAccount(
final Address address, final long nonce, final Wei balance, final Hash codeHash) {
final Address address, final long nonce, final Wei balance) {
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);
Bytes basicDataLeafValue = SuffixTreeEncoder.encodeVersion(Bytes32.ZERO);
basicDataLeafValue = basicDataLeafValue.or(SuffixTreeEncoder.encodeNonce(toLittleEndian(UInt256.valueOf(nonce))));
basicDataLeafValue = basicDataLeafValue.or(SuffixTreeEncoder.encodeBalance(toLittleEndian(balance)));
keyValues.put(trieKeyAdapter.basicDataKey(address), basicDataLeafValue);
return keyValues;
}

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));
keys.add(trieKeyAdapter.basicDataKey(address));
return keys;
}

public Map<Bytes, Bytes> generateKeyValuesForCode(final Address address, final Bytes code) {
public Map<Bytes, Bytes> generateKeyValuesForCode(final Address address, final Bytes code, final Hash codeHash) {
final Map<Bytes, Bytes> keyValues = new HashMap<>();
keyValues.put(
trieKeyAdapter.codeSizeKey(address), toLittleEndian(UInt256.valueOf(code.size())));
trieKeyAdapter.basicDataKey(address), SuffixTreeEncoder.encodeCodeSize(toLittleEndian(UInt256.valueOf(code.size()))));
keyValues.put(trieKeyAdapter.codeHashKey(address), codeHash);
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));
Expand All @@ -71,8 +70,8 @@ public Map<Bytes, Bytes> generateKeyValuesForCode(final Address address, final B

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));
keys.add(trieKeyAdapter.basicDataKey(address));
keys.add(trieKeyAdapter.codeHashKey(address));
List<UInt256> codeChunks = trieKeyAdapter.chunkifyCode(code);
for (int i = 0; i < codeChunks.size(); i++) {
keys.add(trieKeyAdapter.codeChunkKey(address, UInt256.valueOf(i)));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.hyperledger.besu.ethereum.verkletrie.util;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;

public class SuffixTreeEncoder {
private static final int VERSION_OFFSET = 0;
private static final int CODE_SIZE_OFFSET = 5;
private static final int NONCE_OFFSET = 8;
private static final int BALANCE_OFFSET = 16;

public static Bytes32 encodeVersion(final Bytes version) {
return encodeIntoBasicDataLeaf(version, VERSION_OFFSET);
}

public static Bytes32 encodeCodeSize(final Bytes version) {
return encodeIntoBasicDataLeaf(version, CODE_SIZE_OFFSET);
}

public static Bytes32 encodeNonce(final Bytes version) {
return encodeIntoBasicDataLeaf(version, NONCE_OFFSET);
}

public static Bytes32 encodeBalance(final Bytes version) {
return encodeIntoBasicDataLeaf(version, BALANCE_OFFSET);
}

/**
* Encoding of a field into the BasicDataLeaf 32 byte value, using Little-Endian order.
* @param value to encode into a 32 byte value
* @param byteShift byte position of `value` within the final 32 byte value
* @throws IllegalArgumentException if `value` does not fit within 32 bytes after being encoded
* @return encoded BasicDataLeaf value
*/
public static Bytes32 encodeIntoBasicDataLeaf(final Bytes value, final int byteShift) {
Bytes32 value32Bytes = Bytes32.leftPad(value);
if (byteShift == 0) {
return value32Bytes;
} else if (byteShift > 0 && value.size() + byteShift <= 32) {
return value32Bytes.shiftLeft(byteShift * 8);
}
throw new IllegalArgumentException("invalid byteShift " + byteShift + " parameter");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.hyperledger.besu.ethereum.verkletrie.util;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.stream.Stream;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

public class SuffixTreeEncoderTest {

public static Stream<Arguments> valuesStart() {
return Stream.of(
Arguments.of(Bytes32.ZERO, Bytes32.ZERO),
Arguments.of(Bytes.of(0xff), Bytes32.fromHexString("0x00000000000000000000000000000000000000000000000000000000000000FF")),
Arguments.of(Bytes.repeat((byte) 0xff, 12), Bytes32.fromHexString("0x0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF")),
Arguments.of(Bytes.fromHexString("0xadef"), Bytes32.fromHexString("0x000000000000000000000000000000000000000000000000000000000000ADEF")),
Arguments.of(Bytes.fromHexString("0x1123d3"), Bytes32.fromHexString("0x00000000000000000000000000000000000000000000000000000000001123D3"))
);
}

public static Stream<Arguments> valuesMiddle() {
return Stream.of(
Arguments.of(Bytes32.ZERO, Bytes32.ZERO),
Arguments.of(Bytes.of(0xff), Bytes32.fromHexString("0x00000000000000000000000000000000FF000000000000000000000000000000")),
Arguments.of(Bytes.repeat((byte) 0xff, 12), Bytes32.fromHexString("0x00000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000")),
Arguments.of(Bytes.fromHexString("0xadef"), Bytes32.fromHexString("0x000000000000000000000000000000ADEF000000000000000000000000000000")),
Arguments.of(Bytes.fromHexString("0x1123d3"), Bytes32.fromHexString("0x0000000000000000000000000000001123D30000000000000000000000000000"))
);
}

public static Stream<Arguments> valuesEnd() {
return Stream.of(
Arguments.of(Bytes32.ZERO, Bytes32.ZERO),
Arguments.of(Bytes.repeat((byte) 0xff, 12), Bytes32.fromHexString("0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000")),
Arguments.of(Bytes.of(0xff), Bytes32.fromHexString("0xFF00000000000000000000000000000000000000000000000000000000000000")),
Arguments.of(Bytes.fromHexString("0xadef"), Bytes32.fromHexString("0xADEF000000000000000000000000000000000000000000000000000000000000")),
Arguments.of(Bytes.fromHexString("0x1123d3"), Bytes32.fromHexString("0x1123D30000000000000000000000000000000000000000000000000000000000"))
);
}

@ParameterizedTest
@MethodSource("valuesStart")
void encodeBytesStart(final Bytes value, final Bytes32 expected) {
assertEquals(expected,
SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, 0));
}

@ParameterizedTest
@MethodSource("valuesMiddle")
void encodeBytesMiddle(final Bytes value, final Bytes32 expected) {
assertEquals(expected,
SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, (32 - value.size()) / 2));
}

@ParameterizedTest
@MethodSource("valuesEnd")
void encodeBytesEnd(final Bytes value, final Bytes32 expected) {
assertEquals(expected,
SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, 32 - value.size()));
}

@ParameterizedTest
@MethodSource("valuesStart")
void encodeBytesOutsideRange(final Bytes value, final Bytes32 ignoredExpected) {
assertThrows(IllegalArgumentException.class, () -> SuffixTreeEncoder.encodeIntoBasicDataLeaf(value, 32));
}
}

0 comments on commit a12ee48

Please sign in to comment.