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

Add no key hashing option to ProofMapIndexProxy [ECR-3779] #1222

Merged
merged 15 commits into from
Nov 21, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion exonum-java-binding/core/checkstyle-suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<!-- Do not require Javadoc for native adapters -->
<suppress files="service/adapters.+.java" checks="JavadocMethod"/>

<suppress files="ProofMapIndexProxyIntegrationTest\.java"
<suppress files="ProofMapIndexProxyNoKeyHashingIntegrationTest\.java"
checks="Javadoc.*"/>

<!-- Allow `aBlock` name -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,27 @@
*
* <p>This map is implemented as a Merkle-Patricia tree. It does not permit null keys and values.
*
* <p>This map can be instantiated in two ways - either with keys hashing with methods
* <p>The Merkle-Patricia tree backing the proof map uses internal 32-byte keys. The tree balance
* relies on the internal keys being uniformly distributed.
*
* <h3><a name="key-hashing">Key hashing in proof maps</a></h3>
*
* <p>By default, when creating the proof map using methods
* {@link #newInstance(String, View, Serializer, Serializer)} and
* {@link #newInGroupUnsafe(String, byte[], View, Serializer, Serializer)} or with no key hashing
* with methods {@link #newInstanceNoKeyHashing(String, View, Serializer, Serializer)} and
* {@link #newInGroupUnsafeNoKeyHashing(String, byte[], View, Serializer, Serializer)}. In case of
* no key hashing keys are required to be 32-byte long. Note that the former option is considered
* a default one.
* {@link #newInGroupUnsafe(String, byte[], View, Serializer, Serializer)}, the user keys are
* converted into internal keys through hashing. This allows to use keys of arbitrary size and
* ensures the balance of the internal tree. It is also possible to create a proof map that will
* not hash keys with methods {@link #newInstanceNoKeyHashing(String, View, Serializer, Serializer)}
* and {@link #newInGroupUnsafeNoKeyHashing(String, byte[], View, Serializer, Serializer)}. In this
* mode, the map will use the user keys as internal tree keys. Such mode of operation is
* appropriate iff all of the following conditions hold:
*
* <ul>
* <li>All keys are 32-byte long</li>
* <li>The keys are uniformly distributed</li>
* <li>The keys come from a trusted source that cannot influence their distribution and affect
* the tree balance.</li>
* </ul>
*
* <p>The "destructive" methods of the map, i.e., the one that change the map contents,
* are specified to throw {@link UnsupportedOperationException} if
Expand All @@ -71,7 +85,7 @@
*/
public final class ProofMapIndexProxy<K, V> extends AbstractIndexProxy implements MapIndex<K, V> {

private final ProofMapKeyCheckingSerializerDecorator<K> keySerializer;
private final Serializer<K> keySerializer;
private final CheckingSerializerDecorator<V> valueSerializer;

/**
Expand Down Expand Up @@ -99,8 +113,9 @@ public static <K, V> ProofMapIndexProxy<K, V> newInstance(
}

/**
* Creates a ProofMapIndexProxy with no key hashing. Requires that keys are 32-byte long.
* Creates a non-key-hashing ProofMapIndexProxy. Requires that keys are 32-byte long.
*
* <p>See more on <a href="ProofMapIndexProxy.html#key-hashing">key hashing in proof maps</a>.
* @param name a unique alphanumeric non-empty identifier of this map in the underlying storage:
* [a-zA-Z0-9_]
* @param view a database view. Must be valid.
Expand All @@ -112,7 +127,6 @@ public static <K, V> ProofMapIndexProxy<K, V> newInstance(
* @throws IllegalStateException if the view is not valid
* @throws IllegalArgumentException if the name is empty
* @see StandardSerializers
* @see ProofMapIndexProxy
*/
public static <K, V> ProofMapIndexProxy<K, V> newInstanceNoKeyHashing(
String name, View view, Serializer<K> keySerializer, Serializer<V> valueSerializer) {
Expand Down Expand Up @@ -153,11 +167,13 @@ public static <K, V> ProofMapIndexProxy<K, V> newInGroupUnsafe(
}

/**
* Creates a new proof map in a <a href="package-summary.html#families">collection group</a>
* Creates a new non-key-hashing proof map in a <a href="package-summary.html#families">collection group</a>
* with the given name. Requires that keys are 32-byte long.
*
* <p>See a <a href="package-summary.html#families-limitations">caveat</a> on index identifiers.
*
* <p>See more on <a href="ProofMapIndexProxy.html#key-hashing">key hashing in proof maps</a>.
*
* @param groupName a name of the collection group
* @param mapId an identifier of this collection in the group, see the caveats
* @param view a database view
Expand All @@ -169,7 +185,6 @@ public static <K, V> ProofMapIndexProxy<K, V> newInGroupUnsafe(
* @throws IllegalStateException if the view is not valid
* @throws IllegalArgumentException if the name or index id is empty
* @see StandardSerializers
* @see ProofMapIndexProxy
*/
public static <K, V> ProofMapIndexProxy<K, V> newInGroupUnsafeNoKeyHashing(
String groupName, byte[] mapId, View view, Serializer<K> keySerializer,
Expand Down Expand Up @@ -200,7 +215,7 @@ private static <K, V> ProofMapIndexProxy<K, V> checkCachedInstance(StorageIndex
private static <K, V> ProofMapIndexProxy<K, V> newMapIndexProxy(IndexAddress address, View view,
Serializer<K> keySerializer, Serializer<V> valueSerializer,
LongSupplier nativeMapConstructor, boolean keyHashing) {
ProofMapKeyCheckingSerializerDecorator<K> ks = decorateKeySerializer(keySerializer, keyHashing);
Serializer<K> ks = decorateKeySerializer(keySerializer, keyHashing);
CheckingSerializerDecorator<V> vs = CheckingSerializerDecorator.from(valueSerializer);

NativeHandle mapNativeHandle = createNativeMap(view, nativeMapConstructor);
Expand All @@ -210,14 +225,12 @@ private static <K, V> ProofMapIndexProxy<K, V> newMapIndexProxy(IndexAddress add
return map;
}

private static <K> ProofMapKeyCheckingSerializerDecorator<K> decorateKeySerializer(
private static <K> Serializer<K> decorateKeySerializer(
Serializer<K> keySerializer, boolean keyHashing) {
if (!keyHashing) {
ProofMapKeySizeCheckingSerializerDecorator<K> sizeCheckingSerializerDecorator =
ProofMapKeySizeCheckingSerializerDecorator.from(keySerializer);
return ProofMapKeyCheckingSerializerDecorator.from(sizeCheckingSerializerDecorator);
} else {
return ProofMapKeyCheckingSerializerDecorator.from(keySerializer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed at all if it can return just keySerializer, with no extra decorator types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To check for not null values?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is one for that already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

com.exonum.binding.common.serialization.CheckingSerializerDecorator

} else {
return CheckingSerializerDecorator.from(keySerializer);
}
}

Expand All @@ -236,7 +249,7 @@ private static native long nativeCreateInGroup(String groupName, byte[] mapId,
long viewNativeHandle, boolean keyHashing);

private ProofMapIndexProxy(NativeHandle nativeHandle, IndexAddress address, View view,
ProofMapKeyCheckingSerializerDecorator<K> keySerializer,
Serializer<K> keySerializer,
CheckingSerializerDecorator<V> valueSerializer) {
super(nativeHandle, address, view);
this.keySerializer = keySerializer;
Expand All @@ -258,7 +271,8 @@ public boolean containsKey(K key) {
* @param value a storage value to associate with the key
* @throws IllegalStateException if this map is not valid
* @throws IllegalArgumentException if the size of the key is not 32 bytes (in case of a
* no key hashing proof map)
* non-key-hashing proof map, see
* <a href="ProofMapIndexProxy.html#key-hashing">key hashing in proof maps</a>)
* @throws UnsupportedOperationException if this map is read-only
*/
@Override
Expand Down Expand Up @@ -302,7 +316,8 @@ public V get(K key) {
* @param otherKeys other proof map keys which might be mapped to some values
* @throws IllegalStateException if this map is not valid
* @throws IllegalArgumentException if the size of any of the keys is not 32 bytes (in case of a
* no key hashing proof map)
* non-key-hashing proof map, see
* <a href="ProofMapIndexProxy.html#key-hashing">key hashing in proof maps</a>)
*/
public UncheckedMapProof getProof(K key, K... otherKeys) {
if (otherKeys.length == 0) {
Expand All @@ -320,7 +335,9 @@ public UncheckedMapProof getProof(K key, K... otherKeys) {
* @param keys proof map keys which might be mapped to some values
* @throws IllegalStateException if this map is not valid
* @throws IllegalArgumentException if the size of any of the keys is not 32 bytes (in case of a
* no key hashing proof map) or keys collection is empty
* non-key-hashing proof map, see
* <a href="ProofMapIndexProxy.html#key-hashing">key hashing in proof maps</a>) or keys
* collection is empty
*/
public UncheckedMapProof getProof(Collection<? extends K> keys) {
checkArgument(!keys.isEmpty(), "Keys collection should not be empty");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

package com.exonum.binding.core.storage.indices;

import static com.exonum.binding.core.storage.indices.StoragePreconditions.checkProofKey;
import static com.google.common.base.Preconditions.checkNotNull;

import com.exonum.binding.common.serialization.Serializer;

/**
* A serializer decorator that checks proof map keys are not null.
* A serializer decorator that checks proof map keys for correctness.
*
* @see StoragePreconditions#checkProofKey(byte[])
*/
final class ProofMapKeyCheckingSerializerDecorator<T> implements Serializer<T> {

Expand All @@ -46,12 +49,12 @@ private ProofMapKeyCheckingSerializerDecorator(Serializer<T> delegate) {
@Override
public byte[] toBytes(T proofKey) {
byte[] dbValue = delegate.toBytes(proofKey);
return checkNotNull(dbValue);
return checkProofKey(dbValue);
}

@Override
public T fromBytes(byte[] serializedProofKey) {
checkNotNull(serializedProofKey);
checkProofKey(serializedProofKey);
return delegate.fromBytes(serializedProofKey);
}
}

This file was deleted.

Loading