Skip to content

ADD: PartialTrieView reconstruction from ExecutionWitness #81

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public abstract class Node<V> {
"0x0000000000000000000000000000000000000000000000000000000000000000"
+ "0100000000000000000000000000000000000000000000000000000000000000");

/** A constant representing a compressed commitment to NullNodes */
public static Bytes32 EMPTY_COMMITMENT_COMPRESSED = Bytes32.ZERO;

Optional<?> previous;
boolean dirty;
boolean persisted;
Expand Down Expand Up @@ -237,12 +240,14 @@ public String toDot() {
* @return The low value.
*/
public static Bytes32 getLowValue(Optional<?> value) {
// Low values have a flag at bit 128.
return value
.map(
(v) ->
Bytes32.rightPad(
Bytes.concatenate(Bytes32.rightPad((Bytes) v).slice(0, 16), Bytes.of(1))))
(v) -> {
byte[] array = new byte[32];
array[16] = 1; // Low values have a isNotNull flag at bit 128
System.arraycopy(((Bytes32) v).toArrayUnsafe(), 0, array, 0, 16);
return Bytes32.wrap(array);
})
.orElse(Bytes32.ZERO);
}

Expand All @@ -254,7 +259,16 @@ public static Bytes32 getLowValue(Optional<?> value) {
*/
public static Bytes32 getHighValue(Optional<?> value) {
return value
.map((v) -> Bytes32.rightPad(Bytes32.rightPad((Bytes) v).slice(16, 16)))
.map(Bytes.class::cast)
.map(
(v) -> {
final byte[] array = new byte[32];
final int size = v.size();
if (size > 16) {
System.arraycopy(((Bytes32) v).toArrayUnsafe(), 16, array, 0, size - 16);
}
return Bytes32.wrap(array);
})
.orElse(Bytes32.ZERO);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
*/
public class StemNode<V> extends BranchNode<V> {

/** A constant representing a StemNode's extension Marker */
public static Bytes32 MARKER = Bytes32.fromHexString("0x01");

private final Bytes stem;
private Optional<Bytes32> leftHash;
private Optional<Bytes> leftCommitment;
Expand Down Expand Up @@ -212,7 +215,6 @@ public Optional<Bytes> getRightCommitment() {
* @return The updated Node
*/
@Override
@SuppressWarnings("unchecked")
public StemNode<V> replaceLocation(Bytes newLocation) {
List<Node<V>> newChildren = new ArrayList<>(maxChild());
for (int i = 0; i < maxChild(); i++) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright Hyperledger Besu Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.ethereum.trie.verkle.proof;

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

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

public record ExecutionWitness(
Bytes32 previousStateRoot,
List<StemInfo> stemInfos,
VerificationHint verificationHint,
IPAMultiProof proof) {

public record SuffixDiff(int suffix, Bytes value) {}
;

public record StemInfo(Bytes stem, int depth, int extensionType, List<SuffixDiff> suffixDiffs) {}
;

public record VerificationHint(List<Bytes32> commitments, List<Bytes> otherStems) {}
;

public record IPAMultiProof(
Bytes32 multiCommitment,
List<Bytes32> leftCommitments,
List<Bytes32> rightCommitments,
Bytes32 finalEvaluation) {

public int getIPADepth() {
return leftCommitments.size();
}

public Bytes toBytes() {
List<Bytes> allBytes = new ArrayList<Bytes>(getIPADepth() * 2 + 1);
allBytes.addAll(leftCommitments);
allBytes.addAll(rightCommitments);
allBytes.add(finalEvaluation);
return Bytes.concatenate(allBytes);
}
}
;

// public static ExecutionWitness fromParameters(ExecutionWitnessParameters
// params) {
// Logic to validate an executionWitness from json parameters.
// }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright Hyperledger Besu Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.ethereum.trie.verkle.proof;

import org.hyperledger.besu.ethereum.trie.verkle.node.Node;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;

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

public class PartialTrieView {
private final Map<Bytes, NodeView> nodeViews;
private final Map<Bytes, StemView> stemViews;

public PartialTrieView(Map<Bytes, NodeView> nodeViews, Map<Bytes, StemView> stemViews) {
this.nodeViews = nodeViews;
this.stemViews = stemViews;
}

public Map<Bytes, NodeView> getNodeViews() {
return nodeViews;
}

public Map<Bytes, StemView> getStemViews() {
return stemViews;
}

public static PartialTrieView fromExecutionWitness(ExecutionWitness executionWitness)
throws IllegalArgumentException {
Map<Bytes, NodeView> nodeViews = new HashMap<>();
Map<Bytes, StemView> stemViews = new HashMap<>();
Iterator<Bytes32> iterCommitments =
executionWitness.verificationHint().commitments().iterator();
Iterator<Bytes> iterOtherStems = executionWitness.verificationHint().otherStems().iterator();

// Process root Node
Bytes stem;
Bytes path = Bytes.EMPTY;
Bytes32 commitment = executionWitness.previousStateRoot();
nodeViews.put(path, NodeView.fromCommitment(commitment));

// Process Stems
for (ExecutionWitness.StemInfo stemInfo : executionWitness.stemInfos()) {
// Process Internal Nodes
for (int i = 1; i < stemInfo.depth(); i++) {
path = stemInfo.stem().slice(0, i);
NodeView current = nodeViews.get(path);
if (current == null) {
if (!iterCommitments.hasNext()) {
throw new IllegalArgumentException("Not enough Commitments");
}
commitment = iterCommitments.next();
nodeViews.put(path, NodeView.fromCommitment(commitment));
}
}

// Process Last Node
// 0: Internal Node with Emtpy value
// 1: Stem Node with different stem
// 2. Stem Node with value
path = stemInfo.stem().slice(0, stemInfo.depth());
switch (stemInfo.extensionType()) {
case 0: // Empty Internal Node
nodeViews.put(path, NodeView.fromCommitment(Node.EMPTY_COMMITMENT_COMPRESSED));
break;
case 1: // Other Stem
if (!iterCommitments.hasNext()) {
throw new IllegalArgumentException("Not enough Commitments");
}
commitment = iterCommitments.next();
if (!iterOtherStems.hasNext()) {
throw new IllegalArgumentException("Not enough OtherStems");
}
stem = iterOtherStems.next();
if (stem.commonPrefix(path) != path) {
throw new IllegalArgumentException("OtherStem does not match path prefix");
}
StemView stemView = stemViews.get(stem);
if (stemView == null) {
stemViews.put(stem, StemView.fromEmpty());
}
break;
case 2: // Stem is Present
stem = stemInfo.stem();
if (!iterCommitments.hasNext()) {
throw new IllegalArgumentException("Not enough Commitments");
}
commitment = iterCommitments.next();
nodeViews.put(path, new NodeView(commitment, stem));

List<ExecutionWitness.SuffixDiff> diffs = stemInfo.suffixDiffs();
Optional<Bytes32> leftCommitment = Optional.empty();
Optional<Bytes32> rightCommitment = Optional.empty();
if (diffs.get(0).suffix() < 128) {
if (!iterCommitments.hasNext()) {
throw new IllegalArgumentException(); // Missing commitments to fill the tree
}
leftCommitment = Optional.of(iterCommitments.next());
}
if (diffs.getLast().suffix() >= 128) {
if (!iterCommitments.hasNext()) {
throw new IllegalArgumentException(); // Missing commitments to fill the tree
}
rightCommitment = Optional.of(iterCommitments.next());
}
stemViews.put(stem, new StemView(leftCommitment, rightCommitment, diffs));
break;
default:
throw new IllegalArgumentException("Illegal ExtensionType");
}
} // End for each stemInfo

if (iterCommitments.hasNext()) {
throw new IllegalArgumentException("Too many commitments");
}
if (iterOtherStems.hasNext()) {
throw new IllegalArgumentException("Too many otherStems");
}
return new PartialTrieView(nodeViews, stemViews);
}

public record NodeView(Bytes32 commitment, Bytes stem) {
public static NodeView fromCommitment(Bytes32 commitment) {
return new NodeView(commitment, Bytes.EMPTY);
}
}
;

public record StemView(
Optional<Bytes32> leftCommiment,
Optional<Bytes32> rightCommitment,
List<ExecutionWitness.SuffixDiff> suffixDiffs) {
public static StemView fromEmpty() {
return new StemView(
Optional.empty(), Optional.empty(), new ArrayList<ExecutionWitness.SuffixDiff>());
}
}
;
}

This file was deleted.

Loading
Loading