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

Implement incremental packed list (basic type list/vector) hashing #122

Merged
merged 5 commits into from
Apr 23, 2019
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
6 changes: 6 additions & 0 deletions ssz/src/main/java/org/ethereum/beacon/ssz/SSZSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.consensys.cava.bytes.Bytes;
import org.ethereum.beacon.ssz.access.SSZField;
import org.ethereum.beacon.ssz.creator.CompositeObjCreator;
import org.ethereum.beacon.ssz.type.SSZListType;
import org.ethereum.beacon.ssz.type.SSZType;
import org.ethereum.beacon.ssz.type.TypeResolver;
import org.ethereum.beacon.ssz.visitor.SSZSimpleDeserializer;
Expand Down Expand Up @@ -46,6 +47,11 @@ public SSZSerializerResult visitAny(SSZType sszType, Object value) {
return sszVisitorHost.handleAny(sszType, value, new SSZSimpleSerializer());
}

@Override
public SSZSerializerResult visitList(SSZListType descriptor, Object listValue, int startIdx, int len) {
return sszVisitorHost.handleSubList(descriptor, listValue, startIdx, len, new SSZSimpleSerializer());
}

/**
* Restores data instance from serialization data using {@link CompositeObjCreator}
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package org.ethereum.beacon.ssz.visitor;

import static java.lang.Math.min;
import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.ethereum.beacon.ssz.incremental.ObservableComposite;
import org.ethereum.beacon.ssz.incremental.UpdateListener;
import org.ethereum.beacon.ssz.type.SSZCompositeType;
Expand Down Expand Up @@ -64,10 +67,8 @@ public MerkleTrie visitComposite(SSZCompositeType type, Object rawValue,
tracker.merkleTree = super.visitComposite(type, rawValue, childVisitor);
} else if (!tracker.elementsUpdated.isEmpty()){
if (type.isList() && ((SSZListType) type).getElementType().isBasicType()) {
// tracker.merkleTree =
// updatePackedTrie(value, childVisitor, tracker.merkleTree, tracker.elementsUpdated);
// TODO fallback to full recalculation for now
tracker.merkleTree = super.visitComposite(type, rawValue, childVisitor);
tracker.merkleTree =
updatePackedTrie((SSZListType) type, rawValue, tracker.merkleTree, tracker.elementsUpdated);
} else {
tracker.merkleTree =
updateNonPackedTrie(type, rawValue, childVisitor, tracker.merkleTree, tracker.elementsUpdated);
Expand All @@ -88,19 +89,54 @@ private MerkleTrie updateNonPackedTrie(
MerkleTrie merkleTree,
SortedSet<Integer> elementsUpdated) {

int newChildrenCount = type.getChildrenCount(value);
MerkleTrie newTrie = copyWithSize(merkleTree, newChildrenCount);
int newChunksCount = newTrie.nodes.length / 2;
return updateTrie(
type,
value,
idx -> childVisitor.apply(idx, type.getChild(value, idx)).getFinalRoot(),
type.getChildrenCount(value),
merkleTree,
elementsUpdated);
}

private MerkleTrie updatePackedTrie(
SSZListType type,
Object value,
MerkleTrie oldTrie,
SortedSet<Integer> elementsUpdated) {

int typeSize = type.getElementType().getSize();
int valsPerChunk = bytesPerChunk / typeSize;

return updateTrie(
type,
value,
idx -> serializePackedChunk(type, value, idx),
(type.getChildrenCount(value) - 1) / valsPerChunk + 1,
oldTrie,
elementsUpdated.stream().map(i -> i / valsPerChunk).distinct().collect(toList()));
}

private MerkleTrie updateTrie(
SSZCompositeType type,
Object value,
Function<Integer, BytesValue> childChunkSupplier,
int newChunksCount,
MerkleTrie oldTrie,
Collection<Integer> chunksUpdated) {

MerkleTrie newTrie = copyWithSize(oldTrie, newChunksCount);
int newTrieWidth = newTrie.nodes.length / 2;

int pos = newChunksCount;
int pos = newTrieWidth;

List<Integer> elementsToRecalc = new ArrayList<>();
for (int i: elementsUpdated) {
if (i < newChunksCount) {
for (int i: chunksUpdated) {
if (i < newTrieWidth) {
elementsToRecalc.add(i);
if (i < newChildrenCount) {
MerkleTrie childHash = childVisitor.apply(i, type.getChild(value, i));
newTrie.nodes[pos + i] = childHash.getFinalRoot();
if (i < newChunksCount) {
newTrie.nodes[pos + i] = childChunkSupplier.apply(i);
} else {
newTrie.nodes[pos + i] = getZeroHash(0);
}
}
}
Expand Down Expand Up @@ -130,15 +166,6 @@ private MerkleTrie updateNonPackedTrie(
return newTrie;
}

private MerkleTrie updatePackedTrie(
SSZCompositeType type,
Object value,
Function<Long, MerkleTrie> childVisitor,
MerkleTrie merkleTree,
SortedSet<Integer> elementsUpdated) {

throw new UnsupportedOperationException();
}
private MerkleTrie copyWithSize(MerkleTrie trie, int newChunksCount) {
int newSize = (int) nextPowerOf2(newChunksCount) * 2;
if (newSize == trie.nodes.length) {
Expand All @@ -162,4 +189,21 @@ private MerkleTrie copyWithSize(MerkleTrie trie, int newChunksCount) {
return new MerkleTrie(newNodes);
}
}

private BytesValue serializePackedChunk(SSZListType basicListType, Object listValue, int chunkIndex) {
int typeSize = basicListType.getElementType().getSize();
int valsPerChunk = bytesPerChunk / typeSize;
if (valsPerChunk * typeSize != bytesPerChunk) {
throw new UnsupportedOperationException("Type size (" + typeSize
+ ") which is not a factor of hasher chunk size (" + bytesPerChunk + ") is not yet supported.");
}
int idx = chunkIndex * valsPerChunk;
int len = Math.min(valsPerChunk, basicListType.getChildrenCount(listValue) - idx);
BytesValue chunk = serializer.visitList(basicListType, listValue, idx, len)
.getSerializedBody();
if (len < valsPerChunk) {
chunk = BytesValue.concat(chunk, BytesValue.wrap(new byte[bytesPerChunk - chunk.size()]));
}
return chunk;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,24 @@ public SSZSerializerResult visitList(SSZListType type, Object param,
return visitComposite(type, param, childVisitor);
}

@Override
public SSZSerializerResult visitSubList(SSZListType type, Object param,
int startIdx, int len, ChildVisitor<Object, SSZSerializerResult> childVisitor) {
return visitComposite(type, param, childVisitor, startIdx, len);
}

@Override
public SSZSerializerResult visitComposite(SSZCompositeType type, Object rawValue,
ChildVisitor<Object, SSZSerializerResult> childVisitor) {
return visitComposite(type, rawValue, childVisitor, 0, type.getChildrenCount(rawValue));
}

private SSZSerializerResult visitComposite(SSZCompositeType type, Object rawValue,
ChildVisitor<Object, SSZSerializerResult> childVisitor, int startIdx, int len) {
List<BytesValue> childSerializations = new ArrayList<>();
boolean fixedSize = type.isFixedSize();
int length = 0;
for (int i = 0; i < type.getChildrenCount(rawValue); i++) {
for (int i = startIdx; i < startIdx + len; i++) {
SSZSerializerResult res = childVisitor.apply(i, type.getChild(rawValue, i));
childSerializations.add(res.serializedLength);
childSerializations.add(res.serializedBody);
Expand Down
23 changes: 16 additions & 7 deletions ssz/src/main/java/org/ethereum/beacon/ssz/visitor/SSZVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,22 @@ default ResultType visitList(
return visitComposite(type, param, childVisitor);
}

/**
* Invoked on the SSZ Container in the type hierarchy
*
* NOTE: you should either implement {@link #visitComposite(SSZCompositeType, Object, ChildVisitor)}
* method or both {@link #visitList(SSZListType, Object, ChildVisitor)} and
* {@link #visitContainer(SSZContainerType, Object, ChildVisitor)} method
*/
default ResultType visitSubList(
SSZListType type,
ParamType param,
int startIdx,
int len,
ChildVisitor<ParamType, ResultType> childVisitor) {
throw new UnsupportedOperationException();
}

/**
* Invoked on the SSZ Container in the type hierarchy
*
* NOTE: you should either implement {@link #visitComposite(SSZCompositeType, Object, ChildVisitor)}
* method or both {@link #visitList(SSZListType, Object, ChildVisitor)} and
* {@link #visitContainer(SSZContainerType, Object, ChildVisitor)} method
*/
default ResultType visitContainer(
SSZContainerType type, ParamType param, ChildVisitor<ParamType, ResultType> childVisitor) {
return visitComposite(type, param, childVisitor);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.ethereum.beacon.ssz.visitor;

import org.ethereum.beacon.ssz.type.SSZListType;
import org.ethereum.beacon.ssz.type.SSZType;

/**
Expand All @@ -8,4 +9,6 @@
public interface SSZVisitorHandler<ResultType> {

ResultType visitAny(SSZType descriptor, Object value);

ResultType visitList(SSZListType descriptor, Object listValue, int startIdx, int len);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,11 @@ public <ResultType, ParamType> ResultType handleAny(
throw new IllegalArgumentException("Unknown type: " + type);
}
}

public <ResultType, ParamType> ResultType handleSubList(
SSZListType type, ParamType value, int startIdx, int len, SSZVisitor<ResultType, ParamType> visitor) {

return visitor.visitSubList(type, value, startIdx, len, (idx, param) ->
handleAny(type.getElementType(), param, visitor));
}
}
Loading