-
Notifications
You must be signed in to change notification settings - Fork 29
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
Conversation
Github shows the modifications weirdly, use |
* and requires that keys are 32-byte long. | ||
* <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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with..with...
how about : ...either with keys hashing using methods...
* [(00…0PK1, V1), (00…0PK2, V2), … (00…0PKi, Vi)]. | ||
*/ | ||
List<MapEntry<HashCode, String>> createSortedMapEntries() { | ||
// Use PROOF_KEYS which are already sorted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation and documentation are not correct. Nowhere the base class says that getProofKeys are expected to be sorted
and how. The usages of this method must be reviewed to determine if they need sorting.
This method/getProofKeys and documentation must be updated accordingly.
} | ||
|
||
/** | ||
* Creates 257 entries for a ProofMap that, when added to it, will make the underlying |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests that rely on keys being equal to proof paths make no sense in the base but
non-hashing flavour of the ProofMap where this property holds. In hashing variant
there is no apparent connection between a key bits and the bits in the corresponding proof path.
The tests that set particular key bits to achieve a certain tree structure to exercise the proof validation must be moved to non-hashing variant.
void getMultiProof_FourEntryMap_DoesNotContain() { | ||
runTestWithView(database::createFork, (map) -> { | ||
/* | ||
This map will have the following structure: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is incorrect in the base, see below.
* {@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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed why it shall be the default option, and the documentation must properly reflect that.
} | ||
|
||
/** | ||
* Creates a ProofMapIndexProxy with no key hashing. Requires that keys are 32-byte long. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to add a short warning with a link to the class-level section?
ProofMapKeySizeCheckingSerializerDecorator.from(keySerializer); | ||
return ProofMapKeyCheckingSerializerDecorator.from(sizeCheckingSerializerDecorator); | ||
} else { | ||
return ProofMapKeyCheckingSerializerDecorator.from(keySerializer); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
* @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 | ||
* @throws IllegalArgumentException if the size of the key is not 32 bytes (in case of a | ||
* no key hashing proof map) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and elsewhere 'a no key hashing proof map' does not sound correct.
- ... a non-key-hashing proof map
- ... a proof map that does not hash keys
- if the size of the key is not 32 bytes and this map uses non-hashed keys ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Maria-Nosyk Would you tell us please which works best?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The third variant is absolutely perfect!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brilliant, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is non-key-hashing proof map
a bad option though? Third variant is a bit too wordy in some cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, the construction you suggest is, first, hard to understand, second, contradicts general requirements to technical documentation. The second variant suggested by @dmitry-timofeev is also acceptable.
@@ -27,6 +27,10 @@ | |||
|
|||
class ProofMapIndexProxyGroupIntegrationTest extends BaseMapIndexGroupTestable<HashCode> { | |||
|
|||
private static final HashCode PK1 = HashCode.fromString(K1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: may just use String
s (Kx are already strings).
|
||
putAll(map, entries); | ||
|
||
Iterator<MapEntry<HashCode, String>> entriesIterator = map.entries(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(keys, values, entries) have incomplete documentation of the order of the elements missing the default variant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional documentation (with the fact that the API users always need to know if hashing happens or not to access the map from other services or verify the proofs it produces) makes me wonder if we shall add two separate types. But that's certainly for a separate issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❕
* A serializer decorator that checks proof map keys for correctness. | ||
* | ||
* @see StoragePreconditions#checkProofKey(byte[]) | ||
* A serializer decorator that checks proof map keys are not null. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert the class to its past form.
I expect this PR to have non-hashing tests Also, the client code in various schemas must be updated to use the appropriate constructor. |
import java.util.stream.Stream; | ||
import org.junit.jupiter.api.Test; | ||
|
||
abstract class BaseProofMapIndexProxyIntegrationTest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This package uses Testable as a suffix to indicate it is not a concrete test but abstract (I don't remember exactly, but I think surefire might also fail to instantiate a non-instantiable 'test' class, as it detects them based on the naming pattern *Test.java).
for (MapEntry<HashCode, String> e : entries) { | ||
assertThat(map, provesThatPresent(e.getKey(), e.getValue())); | ||
} | ||
assertThat(map, provesThatPresent(entries)); | ||
}); | ||
} | ||
|
||
@Test | ||
void getProof_MultiEntryMapDoesNotContain() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test creates 32-byte keys, but it still seems to be appropriate for both proof map cases.
Ok, will disable those, I thought core changes are already merged and we'd be able to integrate this PR with native code, but I see it's still WIP. |
class ProofMapIndexProxyNoKeyHashingIntegrationTest | ||
extends BaseProofMapIndexProxyIntegrationTestable { | ||
|
||
static final List<HashCode> PROOF_KEYS = Stream.of( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TEST_KEYS/SORTED_TEST_KEYS
private static final HashCode INVALID_PROOF_KEY = HashCode.fromString("1234"); | ||
|
||
@Override | ||
List<HashCode> getProofKeys() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: getKeys or getTestKeys
* [(00…0PK1, V1), (00…0PK2, V2), … (00…0PKi, Vi)]. | ||
*/ | ||
List<MapEntry<HashCode, String>> createSortedMapEntries() { | ||
// Use PROOF_KEYS which are already sorted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use them directly instead of interface method, with appropriate
naming there won't be a need for comment
As most of ProofMaps in services and schemas continue using nonHashing, they shall be migrated to that. (The only hashing is probably DispatcherSchema). Separately we might consider migrating some services to the default hashing variant, e.g., QaService with its two maps 🤔 |
Ok, will do that in a separate PR. |
/** | ||
* {@inheritDoc} | ||
* | ||
* The entries are ordered by keys in lexicographical order if this map is a non-key-hashing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... is a <a href="...">non-key-hashing proof map</a>.
I don't think it is reasonable: this one changes the specification of It is 15 seconds search-replace (6 usages in prod code) + 2–5 minutes documenting the non-hashing aspect, where applicable. |
Ok, updated those. Not sure what documentation is applicable for those, other than |
The variant in use must be documented in schemas as they are supposed to provide adequate specification of the service persistent data. Whether or not a proof map hashes its keys affects the clients verifying proofs.
What the API documentation must say is whether or not a hashing variant is used (it doesn't have to explain why). As 32-byte length is not enough to use a non-key-hashing map, as its documentation says, the justification might be either in the API docs or in implementation docs. |
If the proofs included the type of map from which they are created, the specification of key hashing won't be needed. I am not sure if it makes sense to include such flag in the proof. |
@MakarovS please resolve the build. |
Fixed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good, thanks!
If the ordering is the same, then it might make sense to move the tests of iterators back to the base. |
Overview
Add no key hashing option to ProofMapIndexProxy, update tests.
See: https://jira.bf.local/browse/ECR-3779
Definition of Done