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

Chunkify Code #12

Merged
merged 3 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ test {
useJUnitPlatform()
}

sourceSets {
test {
resources {
srcDirs = ['src/test/resources']
}
}
}

dependencies {
implementation "com.google.guava:guava"
implementation 'net.java.dev.jna:jna'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

import org.hyperledger.besu.ethereum.trie.verkle.hasher.Hasher;

import java.util.ArrayList;
import java.util.List;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
Expand All @@ -38,14 +41,14 @@ public class TrieKeyAdapter {
private final UInt256 VERKLE_NODE_WIDTH = UInt256.valueOf(256);
private final UInt256 MAIN_STORAGE_OFFSET = UInt256.valueOf(256).pow(31);

private final Hasher<Bytes32> hasher;
private final Hasher hasher;

/**
* Creates a TrieKeyAdapter with the provided hasher.
*
* @param hasher The hasher used for key generation.
*/
public TrieKeyAdapter(Hasher<Bytes32> hasher) {
public TrieKeyAdapter(Hasher hasher) {
this.hasher = hasher;
}

Expand All @@ -56,51 +59,37 @@ public TrieKeyAdapter(Hasher<Bytes32> hasher) {
* @param subIndex The subIndex.
* @return The modified key.
*/
Bytes32 swapLastByte(Bytes32 base, UInt256 subIndex) {
Bytes32 key =
(Bytes32) Bytes.concatenate(base.slice(0, 31), Bytes.of(subIndex.toBytes().get(31)));
return key;
Bytes32 swapLastByte(Bytes32 base, Bytes subIndex) {
return (Bytes32) Bytes.concatenate(base.slice(0, 31), subIndex);
}

/**
* Generates the base key for a given address and treeIndex.
* Generates a storage key for a given address and storage key.
*
* @param address The address.
* @param treeIndex The tree index.
* @return The generated base key.
* @param storageKey The storage key.
* @return The generated storage key.
*/
Bytes32 baseKey(Bytes32 address, UInt256 treeIndex) {
int type_encoding = 2;
UInt256 encoding =
UInt256.valueOf(type_encoding).add(VERKLE_NODE_WIDTH.multiply(UInt256.valueOf(16)));

Bytes32[] input =
new Bytes32[] {
Bytes32.rightPad(encoding.toBytes().slice(16, 16).reverse()),
Bytes32.rightPad(address.slice(0, 16)),
Bytes32.rightPad(address.slice(16, 16)),
Bytes32.rightPad(treeIndex.toBytes().slice(16, 16).reverse()),
Bytes32.rightPad(treeIndex.toBytes().slice(0, 16).reverse())
};
Bytes32 key = hasher.commit(input);
public Bytes32 storageKey(Bytes address, Bytes32 storageKey) {
UInt256 index = UInt256.fromBytes(storageKey);
UInt256 headerOffset = CODE_OFFSET.subtract(HEADER_STORAGE_OFFSET);
UInt256 offset =
((index.compareTo(headerOffset) < 0) ? HEADER_STORAGE_OFFSET : MAIN_STORAGE_OFFSET);
UInt256 pos = offset.add(index);
Bytes32 base = hasher.trieKeyHash(address, pos.divide(VERKLE_NODE_WIDTH));
Bytes32 key = swapLastByte(base, pos.mod(VERKLE_NODE_WIDTH).toMinimalBytes());
return key;
}

/**
* Generates a storage key for a given address and storage key.
* Generates a code chunk key for a given address and chunkId.
*
* @param address The address.
* @param storageKey The storage key.
* @return The generated storage key.
* @param chunkId The chunk ID.
* @return The generated code chunk key.
*/
public Bytes32 storageKey(Bytes32 address, UInt256 storageKey) {
UInt256 headerOffset = CODE_OFFSET.subtract(HEADER_STORAGE_OFFSET);
UInt256 offset =
((storageKey.compareTo(headerOffset) < 0) ? HEADER_STORAGE_OFFSET : MAIN_STORAGE_OFFSET);
UInt256 pos = offset.add(storageKey);
Bytes32 base = baseKey(address, pos.divide(VERKLE_NODE_WIDTH));
Bytes32 key = swapLastByte(base, pos.mod(VERKLE_NODE_WIDTH));
return key;
public Bytes32 codeChunkKey(Bytes address, long chunkId) {
return codeChunkKey(address, UInt256.valueOf(chunkId));
}

/**
Expand All @@ -110,10 +99,10 @@ public Bytes32 storageKey(Bytes32 address, UInt256 storageKey) {
* @param chunkId The chunk ID.
* @return The generated code chunk key.
*/
public Bytes32 codeChunkKey(Bytes32 address, UInt256 chunkId) {
public Bytes32 codeChunkKey(Bytes address, UInt256 chunkId) {
UInt256 pos = CODE_OFFSET.add(chunkId);
Bytes32 base = baseKey(address, pos.divide(VERKLE_NODE_WIDTH));
Bytes32 key = swapLastByte(base, pos.mod(VERKLE_NODE_WIDTH));
Bytes32 base = hasher.trieKeyHash(address, pos.divide(VERKLE_NODE_WIDTH).toBytes());
Bytes32 key = swapLastByte(base, pos.mod(VERKLE_NODE_WIDTH).toMinimalBytes());
return key;
}

Expand All @@ -124,9 +113,9 @@ public Bytes32 codeChunkKey(Bytes32 address, UInt256 chunkId) {
* @param leafKey The leaf key.
* @return The generated header key.
*/
Bytes32 headerKey(Bytes32 address, UInt256 leafKey) {
Bytes32 base = baseKey(address, UInt256.valueOf(0));
Bytes32 key = swapLastByte(base, leafKey);
Bytes32 headerKey(Bytes address, UInt256 leafKey) {
Bytes32 base = hasher.trieKeyHash(address, UInt256.valueOf(0).toBytes());
Bytes32 key = swapLastByte(base, leafKey.toBytes().slice(31));
return key;
}

Expand All @@ -136,7 +125,7 @@ Bytes32 headerKey(Bytes32 address, UInt256 leafKey) {
* @param address The address.
* @return The generated version key.
*/
public Bytes32 versionKey(Bytes32 address) {
public Bytes32 versionKey(Bytes address) {
return headerKey(address, VERSION_LEAF_KEY);
}

Expand All @@ -146,7 +135,7 @@ public Bytes32 versionKey(Bytes32 address) {
* @param address The address.
* @return The generated balance key.
*/
public Bytes32 balanceKey(Bytes32 address) {
public Bytes32 balanceKey(Bytes address) {
return headerKey(address, BALANCE_LEAF_KEY);
}

Expand All @@ -156,7 +145,7 @@ public Bytes32 balanceKey(Bytes32 address) {
* @param address The address.
* @return The generated nonce key.
*/
public Bytes32 nonceKey(Bytes32 address) {
public Bytes32 nonceKey(Bytes address) {
return headerKey(address, NONCE_LEAF_KEY);
}

Expand All @@ -166,7 +155,7 @@ public Bytes32 nonceKey(Bytes32 address) {
* @param address The address.
* @return The generated code Keccak key.
*/
public Bytes32 codeKeccakKey(Bytes32 address) {
public Bytes32 codeKeccakKey(Bytes address) {
return headerKey(address, CODE_KECCAK_LEAF_KEY);
}

Expand All @@ -176,7 +165,55 @@ public Bytes32 codeKeccakKey(Bytes32 address) {
* @param address The address.
* @return The generated code size key.
*/
public Bytes32 codeSizeKey(Bytes32 address) {
return headerKey(address, CODE_SIZE_LEAF_KEY);
public Bytes32 codeSizeKey(Bytes address) {
return (headerKey(address, CODE_SIZE_LEAF_KEY));
}

/**
* Chunk code's bytecode for insertion in the Trie. Each chunk code uses its position in the list
* as chunkId
*
* @param bytecode Code's bytecode
* @return List of 32-bytes code chunks
*/
public List<Bytes32> chunkifyCode(Bytes bytecode) {
if (bytecode.isEmpty()) {
return new ArrayList<Bytes32>();
}

// Chunking variables
int CHUNK_SIZE = 31;
int nChunks = 1 + ((bytecode.size() - 1) / CHUNK_SIZE);
int padSize = nChunks * CHUNK_SIZE - bytecode.size();
Bytes code = Bytes.concatenate(bytecode, Bytes.repeat((byte) 0, padSize));
List<Bytes32> chunks = new ArrayList<Bytes32>(nChunks);

// OpCodes for PUSH's
int PUSH_OFFSET = 95;
int PUSH1 = PUSH_OFFSET + 1;
int PUSH32 = PUSH_OFFSET + 32;

// Iterator data
int chunkPos = 0; // cursor position to start of current chunk
int posInChunk = 0; // cursor position relative to the current chunk
int nPushData = 0; // number of bytes in current push data

// Create chunk iteratively
for (int chunkId = 0; chunkId < nChunks; ++chunkId) {
chunkPos = chunkId * CHUNK_SIZE;
posInChunk = nPushData;
while (posInChunk < CHUNK_SIZE) {
int opCode = Byte.toUnsignedInt(code.get(chunkPos + posInChunk));
posInChunk += 1;
if (PUSH1 <= opCode && opCode <= PUSH32) {
posInChunk += opCode - PUSH_OFFSET;
}
}
chunks.add(
(Bytes32) Bytes.concatenate(Bytes.of(nPushData), code.slice(chunkPos, CHUNK_SIZE)));
nPushData = posInChunk - CHUNK_SIZE;
}

return chunks;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,26 @@
*/
package org.hyperledger.besu.ethereum.trie.verkle.hasher;

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

/**
* Defines an interface for a Verkle Trie node hashing strategy.
*
* @param <V> The type of values to be hashed.
*/
public interface Hasher<V> {
/** Defines an interface for a Verkle Trie node hashing strategy. */
public interface Hasher {

/**
* Calculates the commitment hash for an array of inputs.
*
* @param inputs An array of values to be hashed.
* @return The commitment hash calculated from the inputs.
*/
public Bytes32 commit(V[] inputs);
public Bytes32 commit(Bytes32[] inputs);

// public Bytes32 commit_sparse(V[] input, int[] index)
/**
* Calculates the hash for an address and index.
*
* @param address Account address.
* @param index Index in storage.
* @return The trie-key hash
*/
public Bytes32 trieKeyHash(Bytes address, Bytes32 index);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* <p>This class implements the Hasher interface and provides a method to commit multiple Bytes32
* inputs using the IPA hashing algorithm.
*/
public class IPAHasher implements Hasher<Bytes32> {
public class PedersenHasher implements Hasher {
/**
* Commits an array of Bytes32 using the IPA hashing algorithm.
*
Expand All @@ -43,4 +43,18 @@ public Bytes32 commit(Bytes32[] inputs) {
Bytes input_serialized = Bytes.concatenate(rev);
return (Bytes32) Bytes32.wrap(LibIpaMultipoint.commit(input_serialized.toArray())).reverse();
}

/**
* Calculates the hash for an address and index.
*
* @param address Account address.
* @param index Index in storage.
* @return The trie-key hash
*/
@Override
public Bytes32 trieKeyHash(Bytes address, Bytes32 index) {
Bytes32 addr = Bytes32.leftPad(address);
Bytes input = Bytes.concatenate(addr, index);
return Bytes32.wrap(LibIpaMultipoint.pedersenHash(input.toArray()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* inputs using the SHA-256 hashing algorithm. It utilizes the Java built-in MessageDigest for
* SHA-256.
*/
public class SHA256Hasher implements Hasher<Bytes32> {
public class SHA256Hasher implements Hasher {
/**
* Commits an array of Bytes32 using the SHA-256 hashing algorithm provided by the MessageDigest.
*
Expand All @@ -48,4 +48,16 @@ public Bytes32 commit(Bytes32[] inputs) {
}
return out;
}

/**
* Calculates the hash for an address and index.
*
* @param address Account address.
* @param index Index in storage.
* @return The trie-key hash
*/
@Override
public Bytes32 trieKeyHash(Bytes address, Bytes32 index) {
return commit(new Bytes32[] {Bytes32.leftPad(address), index});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package org.hyperledger.besu.ethereum.trie.verkle.visitor;

import org.hyperledger.besu.ethereum.trie.verkle.hasher.Hasher;
import org.hyperledger.besu.ethereum.trie.verkle.hasher.IPAHasher;
import org.hyperledger.besu.ethereum.trie.verkle.hasher.PedersenHasher;
import org.hyperledger.besu.ethereum.trie.verkle.node.InternalNode;
import org.hyperledger.besu.ethereum.trie.verkle.node.Node;
import org.hyperledger.besu.ethereum.trie.verkle.node.StemNode;
Expand All @@ -32,7 +32,7 @@
* @param <V> The type of the node's value.
*/
public class HashVisitor<V extends Bytes> implements PathNodeVisitor<V> {
Hasher<Bytes32> hasher = new IPAHasher();
Hasher hasher = new PedersenHasher();

/**
* Visits a internal node, computes its hash, and returns a new internal node with the updated
Expand Down
Loading
Loading