Skip to content

Commit

Permalink
PedersenHash wraps the besu-native pedersen commit and hash function (#…
Browse files Browse the repository at this point in the history
…12)

PedersenHash wraps the besu-native pedersen commit and hash functions.
It is used now in TrieKeyAdaptor to generate trie-keys

Signed-off-by: Thomas Zamojski <[email protected]>
  • Loading branch information
thomas-quadratic authored Nov 16, 2023
1 parent 26921d9 commit cf00bfd
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 67 deletions.
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

0 comments on commit cf00bfd

Please sign in to comment.