Skip to content

Commit

Permalink
ProofMapIndex variants for hashed and raw keys [ECR-3777] (exonum#1531)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-mukhanov authored and Oleksandr Anyshchenko committed Nov 19, 2019
1 parent e06baa3 commit f33e636
Show file tree
Hide file tree
Showing 19 changed files with 1,449 additions and 1,033 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ The project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
- `ProtobufConvert` has been implemented for `MapProof`. (#1512)
- New variant of the `ProofMapIndex` have been introduced - `RawProofMapIndex`.
It is used for keys that maps directly to `ProofPath`, for example `Hash` and
`PublicKey`. (#1531)
- By default `ProofMapIndex` is used for keys that implement `ObjectHash`.
- For `Hash` keys both map variants works the same, because `ObjectHash`
implementation for `Hash` returns the hash itself.
### Internal improvements
#### exonum
Expand Down
15 changes: 10 additions & 5 deletions components/merkledb/benches/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ use criterion::{black_box, Bencher, Criterion};
use failure::{self, format_err};
use rand::{rngs::StdRng, RngCore, SeedableRng};

use exonum_crypto::{self, Hash};
use exonum_crypto::{self, hash, Hash};
use exonum_merkledb::{
impl_object_hash_for_binary_value,
proof_map_index::{BranchNode, ProofPath},
BinaryKey, BinaryValue, ObjectHash,
impl_object_hash_for_binary_value, proof_map_index::BranchNode, BinaryKey, BinaryValue,
ObjectHash,
};

const CHUNK_SIZE: usize = 64;
Expand Down Expand Up @@ -182,7 +181,13 @@ where

fn bench_binary_key_concat(b: &mut Bencher<'_>) {
b.iter_with_setup(
|| ("prefixed.key", Hash::zero(), ProofPath::new(&Hash::zero())),
|| {
(
"prefixed.key",
Hash::zero(),
hash(&[0; 32]), // emulate ProofPath::new(&Hash::zero())).
)
},
|(prefix, key, path)| {
let mut v = vec![0; prefix.size() + key.size() + path.size()];
let mut pos = prefix.write(&mut v);
Expand Down
56 changes: 54 additions & 2 deletions components/merkledb/src/access/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

use super::{Access, AccessError, FromAccess};
use crate::{
views::IndexType, BinaryKey, BinaryValue, Entry, Group, IndexAddress, KeySetIndex, ListIndex,
MapIndex, ObjectHash, ProofListIndex, ProofMapIndex, SparseListIndex, ValueSetIndex,
proof_map_index::{Raw, ToProofPath},
views::IndexType,
BinaryKey, BinaryValue, Entry, Group, IndexAddress, KeySetIndex, ListIndex, MapIndex,
ObjectHash, ProofListIndex, ProofMapIndex, SparseListIndex, ValueSetIndex,
};

/// Extension trait allowing for easy access to indices from any type implementing
Expand Down Expand Up @@ -115,6 +117,56 @@ pub trait AccessExt: Access {
ProofMapIndex::from_access(self, addr.into()).unwrap()
}

/// Variant of the proof map with keys that can be mapped directly to `ProofPath`.
///
/// # Panics
///
/// If the index exists, but is not a Merkelized map.
fn get_raw_proof_map<I, K, V>(self, addr: I) -> ProofMapIndex<Self::Base, K, V, Raw>
where
I: Into<IndexAddress>,
K: BinaryKey,
V: BinaryValue,
Raw: ToProofPath<K>,
{
ProofMapIndex::<_, _, _, Raw>::from_access(self, addr.into()).unwrap()
}

/// Generic variant of the proof map. Requires implicit `KeyMode` to be constructed.
///
/// # Examples
///
/// ```
/// use exonum_merkledb::{access::AccessExt, Fork, Database, ListIndex, TemporaryDB, ProofMapIndex,
/// proof_map_index::{Raw, Hashed}};
/// use exonum_crypto::PublicKey;
///
/// let db = TemporaryDB::new();
/// let fork = db.fork();
///
/// // Hashed variant for keys implementing `ObjectHash`.
/// let hashed_map: ProofMapIndex<&Fork, u32, u32, Hashed> = fork.get_generic_proof_map("hashed");
///
/// // Raw variant for keys that can be mapped directly to `ProofPath`.
/// let raw_map: ProofMapIndex<&Fork, PublicKey, u32, Raw> = fork.get_generic_proof_map("raw");
/// ```
///
/// # Panics
///
/// If the index exists, but is not a Merkelized map.
fn get_generic_proof_map<I, K, V, KeyMode>(
self,
addr: I,
) -> ProofMapIndex<Self::Base, K, V, KeyMode>
where
I: Into<IndexAddress>,
K: BinaryKey,
V: BinaryValue,
KeyMode: ToProofPath<K>,
{
ProofMapIndex::<_, _, _, KeyMode>::from_access(self, addr.into()).unwrap()
}

/// Gets a sparse list index with the specified address.
///
/// # Panics
Expand Down
9 changes: 1 addition & 8 deletions components/merkledb/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use byteorder::{ByteOrder, LittleEndian};
use exonum_crypto::{Hash, HashStream, HASH_SIZE};
use exonum_crypto::{Hash, HashStream};
use failure::Fail;
use hex::FromHex;

Expand Down Expand Up @@ -216,13 +216,6 @@ impl ObjectHash for Hash {
}
}

/// Just returns the origin array.
impl ObjectHash for [u8; HASH_SIZE] {
fn object_hash(&self) -> Hash {
Hash::new(*self)
}
}

/// Errors that can occur while validating a `ListProof` or `MapProof` against
/// a trusted collection hash.
#[derive(Debug, Fail)]
Expand Down
2 changes: 1 addition & 1 deletion components/merkledb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ pub use self::{
#[doc(no_inline)]
pub use self::{
proof_list_index::{ListProof, ProofListIndex},
proof_map_index::{MapProof, ProofMapIndex},
proof_map_index::{MapProof, ProofMapIndex, RawProofMapIndex},
};

#[macro_use]
Expand Down
47 changes: 40 additions & 7 deletions components/merkledb/src/proof_map_index/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::{

use leb128;

use exonum_crypto::HASH_SIZE;
use exonum_crypto::{Hash, PublicKey, HASH_SIZE};

use crate::{BinaryKey, ObjectHash};

Expand Down Expand Up @@ -57,6 +57,44 @@ fn reset_bits(value: &mut u8, pos: u16) {
*value &= reset_bits_mask;
}

/// Hashed variant of proof map key.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Hashed;

/// Raw variant of proof map key.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Raw;

/// Trait defining key transforming function used to transform key to `ProofPath`.
pub trait ToProofPath<K> {
/// Transforms key to `ProofPath`.
fn transform_key(key: &K) -> ProofPath;
}

impl<K: ObjectHash> ToProofPath<K> for Hashed {
fn transform_key(key: &K) -> ProofPath {
ProofPath::from_bytes(key.object_hash())
}
}

impl ToProofPath<PublicKey> for Raw {
fn transform_key(key: &PublicKey) -> ProofPath {
ProofPath::from_bytes(key.as_ref())
}
}

impl ToProofPath<Hash> for Raw {
fn transform_key(key: &Hash) -> ProofPath {
ProofPath::from_bytes(key.as_ref())
}
}

impl ToProofPath<[u8; 32]> for Raw {
fn transform_key(key: &[u8; 32]) -> ProofPath {
ProofPath::from_bytes(key)
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ChildKind {
Left,
Expand Down Expand Up @@ -98,11 +136,6 @@ pub struct ProofPath {
}

impl ProofPath {
/// Creates a path from the given key.
pub fn new(key: &impl ObjectHash) -> Self {
Self::from_bytes(key.object_hash())
}

/// Checks if this is a path to a leaf `ProofMapIndex` node.
pub fn is_leaf(&self) -> bool {
self.bytes[0] == LEAF_KEY_PREFIX
Expand Down Expand Up @@ -469,7 +502,7 @@ mod tests {
let path = ProofPath::from_bytes(&[1; 32]).prefix(3);
assert_eq!(serde_json::to_value(&path).unwrap(), json!("100"));
let path: ProofPath = serde_json::from_value(json!("101001")).unwrap();
assert_eq!(path, ProofPath::new(&[0b_0010_0101; 32]).prefix(6));
assert_eq!(path, Raw::transform_key(&[0b_0010_0101; 32]).prefix(6));

// Fuzz tests for roundtrip.
let mut rng = rand::thread_rng();
Expand Down
Loading

0 comments on commit f33e636

Please sign in to comment.