Skip to content
Snippets Groups Projects
Commit a68036fd authored by Éloïs's avatar Éloïs
Browse files

[feat] blockchain-dal: create req get identities (with filters)

parent 1f0d06bf
No related branches found
No related tags found
1 merge request!110Resolve "Create inter-modules requests to get Identities from blockchain"
Showing
with 323 additions and 4 deletions
...@@ -434,6 +434,7 @@ dependencies = [ ...@@ -434,6 +434,7 @@ dependencies = [
"duniter-module 0.1.0-a0.1", "duniter-module 0.1.0-a0.1",
"duniter-network 0.1.0-a0.1", "duniter-network 0.1.0-a0.1",
"dup-crypto 0.6.0", "dup-crypto 0.6.0",
"durs-blockchain-dal 0.1.0-a0.1",
"durs-network-documents 0.3.1", "durs-network-documents 0.3.1",
"serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
......
...@@ -14,6 +14,7 @@ dup-crypto = { path = "../../tools/crypto" } ...@@ -14,6 +14,7 @@ dup-crypto = { path = "../../tools/crypto" }
dubp-documents= { path = "../../tools/documents" } dubp-documents= { path = "../../tools/documents" }
duniter-module = { path = "../module" } duniter-module = { path = "../module" }
duniter-network = { path = "../network" } duniter-network = { path = "../network" }
durs-blockchain-dal = { path = "../../modules/blockchain/blockchain-dal" }
durs-network-documents = { path = "../../tools/network-documents" } durs-network-documents = { path = "../../tools/network-documents" }
serde = "1.0.*" serde = "1.0.*"
serde_derive = "1.0.*" serde_derive = "1.0.*"
......
...@@ -18,6 +18,7 @@ use dubp_documents::BlockId; ...@@ -18,6 +18,7 @@ use dubp_documents::BlockId;
use duniter_network::requests::OldNetworkRequest; use duniter_network::requests::OldNetworkRequest;
use dup_crypto::hashs::Hash; use dup_crypto::hashs::Hash;
use dup_crypto::keys::*; use dup_crypto::keys::*;
use durs_blockchain_dal::filters::identities::IdentitiesFilter;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Modules request content /// Modules request content
...@@ -49,6 +50,8 @@ pub enum BlockchainRequest { ...@@ -49,6 +50,8 @@ pub enum BlockchainRequest {
Chunk(u64, usize), Chunk(u64, usize),
/// Usernames corresponding to the public keys in parameter /// Usernames corresponding to the public keys in parameter
UIDs(Vec<PubKey>), UIDs(Vec<PubKey>),
/// Get identities
GetIdentities(IdentitiesFilter),
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
......
...@@ -24,6 +24,7 @@ use duniter_module::ModuleReqId; ...@@ -24,6 +24,7 @@ use duniter_module::ModuleReqId;
use duniter_network::requests::NetworkResponse; use duniter_network::requests::NetworkResponse;
use dup_crypto::hashs::Hash; use dup_crypto::hashs::Hash;
use dup_crypto::keys::*; use dup_crypto::keys::*;
use durs_blockchain_dal::entities::identity::DALIdentity;
use std::collections::HashMap; use std::collections::HashMap;
/// Durs request response message /// Durs request response message
...@@ -67,6 +68,8 @@ pub enum BlockchainResponse { ...@@ -67,6 +68,8 @@ pub enum BlockchainResponse {
Chunk(ModuleReqId, Vec<BlockDocument>), Chunk(ModuleReqId, Vec<BlockDocument>),
/// Usernames corresponding to the public keys in parameter /// Usernames corresponding to the public keys in parameter
UIDs(ModuleReqId, HashMap<PubKey, Option<String>>), UIDs(ModuleReqId, HashMap<PubKey, Option<String>>),
/// Identities
Identities(ModuleReqId, Vec<DALIdentity>),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
......
...@@ -21,3 +21,6 @@ pub static DEFAULT_MS_PERIOD: &'static u64 = &5_259_600; ...@@ -21,3 +21,6 @@ pub static DEFAULT_MS_PERIOD: &'static u64 = &5_259_600;
pub static DEFAULT_TX_WINDOW: &'static u64 = &604_800; pub static DEFAULT_TX_WINDOW: &'static u64 = &604_800;
/// Maximum roolback length /// Maximum roolback length
pub static FORK_WINDOW_SIZE: &'static usize = &200; pub static FORK_WINDOW_SIZE: &'static usize = &200;
/// Default page size for requests responses
pub static DEFAULT_PAGE_SIZE: &'static usize = &50;
...@@ -17,7 +17,7 @@ use dubp_documents::documents::identity::IdentityDocument; ...@@ -17,7 +17,7 @@ use dubp_documents::documents::identity::IdentityDocument;
use dubp_documents::{BlockId, Blockstamp}; use dubp_documents::{BlockId, Blockstamp};
use durs_wot::NodeId; use durs_wot::NodeId;
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
/// Identity state /// Identity state
pub enum DALIdentityState { pub enum DALIdentityState {
/// Member /// Member
...@@ -32,7 +32,7 @@ pub enum DALIdentityState { ...@@ -32,7 +32,7 @@ pub enum DALIdentityState {
ImplicitRevoked(Vec<usize>), ImplicitRevoked(Vec<usize>),
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
/// Identity in database /// Identity in database
pub struct DALIdentity { pub struct DALIdentity {
/// Identity hash /// Identity hash
......
// Copyright (C) 2018 The Durs Project Developers.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Define all filters applicable to identities
use super::PagingFilter;
use dup_crypto::keys::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// Identities filter
pub struct IdentitiesFilter {
/// Pagination parameters
pub paging: PagingFilter,
/// Filter identities by public key
pub by_pubkey: Option<PubKey>,
}
impl Default for IdentitiesFilter {
fn default() -> Self {
IdentitiesFilter {
paging: PagingFilter::default(),
by_pubkey: None,
}
}
}
impl IdentitiesFilter {
/// Create "by pubkey" filter
pub fn by_pubkey(pubkey: PubKey) -> Self {
IdentitiesFilter {
paging: PagingFilter::default(),
by_pubkey: Some(pubkey),
}
}
}
// Copyright (C) 2018 The Durs Project Developers.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pub mod identities;
use dubp_documents::BlockId;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// Pagination parameters
pub struct PagingFilter {
/// Retrieve only the elements created after this block
pub from: BlockId,
/// Retrieve only the elements created before this block
pub to: Option<BlockId>,
/// Number of elements per page
pub page_size: usize,
/// Number of the page requested
pub page_number: usize,
}
impl Default for PagingFilter {
fn default() -> Self {
PagingFilter {
from: BlockId(0),
to: None,
page_size: *crate::constants::DEFAULT_PAGE_SIZE,
page_number: 0,
}
}
}
impl PagingFilter {
#[inline]
/// Checks if a given element has been created in the requested period
pub fn check_created_on(&self, created_on: BlockId, current_block_id: BlockId) -> bool {
created_on >= self.from && created_on <= self.to.unwrap_or(current_block_id)
}
#[inline]
/// Checks if a given element index is located in the current page
pub fn is_in_page(&self, i: usize) -> bool {
i >= self.page_size * self.page_number && i < self.page_size * (self.page_number + 1)
}
}
...@@ -39,6 +39,9 @@ pub mod constants; ...@@ -39,6 +39,9 @@ pub mod constants;
/// Contains all entities stored in databases /// Contains all entities stored in databases
pub mod entities; pub mod entities;
/// Define all filters applicable to entities
pub mod filters;
/// Contains all read databases functions /// Contains all read databases functions
pub mod readers; pub mod readers;
......
...@@ -14,11 +14,51 @@ ...@@ -14,11 +14,51 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::entities::identity::DALIdentity; use crate::entities::identity::DALIdentity;
use crate::filters::identities::IdentitiesFilter;
use crate::{BinDB, DALError, IdentitiesV10Datas}; use crate::{BinDB, DALError, IdentitiesV10Datas};
use dubp_documents::{BlockId, Document};
use dup_crypto::keys::*; use dup_crypto::keys::*;
use durs_wot::NodeId; use durs_wot::NodeId;
use std::collections::HashMap; use std::collections::HashMap;
/// Get identities in databases
pub fn get_identities(
db: &BinDB<IdentitiesV10Datas>,
filters: IdentitiesFilter,
current_block_id: BlockId,
) -> Result<Vec<DALIdentity>, DALError> {
if let Some(pubkey) = filters.by_pubkey {
if let Some(idty) = db.read(|db| db.get(&pubkey).cloned())? {
Ok(vec![idty])
} else {
Ok(vec![])
}
} else {
Ok(db.read(|db| {
let mut identities: Vec<&DALIdentity> = db
.values()
.filter(|idty| {
filters
.paging
.check_created_on(idty.idty_doc.blockstamp().id, current_block_id)
})
.collect();
identities.sort_by(|i1, i2| {
i1.idty_doc
.blockstamp()
.id
.cmp(&i2.idty_doc.blockstamp().id)
});
identities
.into_iter()
.skip(filters.paging.page_size * filters.paging.page_number)
.take(filters.paging.page_size)
.cloned()
.collect()
})?)
}
}
/// Get identity in databases /// Get identity in databases
pub fn get_identity( pub fn get_identity(
db: &BinDB<IdentitiesV10Datas>, db: &BinDB<IdentitiesV10Datas>,
...@@ -72,3 +112,105 @@ pub fn get_wot_index( ...@@ -72,3 +112,105 @@ pub fn get_wot_index(
.collect() .collect()
})?) })?)
} }
#[cfg(test)]
mod test {
use super::*;
use crate::entities::identity::*;
use crate::filters::PagingFilter;
use crate::*;
use dubp_documents::Blockstamp;
use dup_crypto_tests_tools::mocks::pubkey;
use rust_tests_tools::collections::slice_same_elems;
fn gen_mock_dal_idty(pubkey: PubKey, created_block_id: BlockId) -> DALIdentity {
DALIdentity {
hash: "".to_owned(),
state: DALIdentityState::Member(vec![]),
joined_on: Blockstamp::default(),
expired_on: None,
revoked_on: None,
idty_doc: dubp_documents_tests_tools::mocks::identity::gen_mock_idty(
pubkey,
created_block_id,
),
wot_id: NodeId(0),
ms_created_block_id: BlockId(0),
ms_chainable_on: vec![],
cert_chainable_on: vec![],
}
}
#[test]
fn test_get_identities() -> Result<(), DALError> {
// Create mock identities
let mock_identities = vec![
gen_mock_dal_idty(pubkey('A'), BlockId(0)),
gen_mock_dal_idty(pubkey('B'), BlockId(1)),
gen_mock_dal_idty(pubkey('C'), BlockId(3)),
gen_mock_dal_idty(pubkey('D'), BlockId(4)),
gen_mock_dal_idty(pubkey('E'), BlockId(5)),
];
// Write mock identities in DB
let identities_db =
BinDB::Mem(open_memory_db::<IdentitiesV10Datas>().expect("Fail to create memory DB !"));
for idty in &mock_identities {
identities_db.write(|db| {
db.insert(idty.idty_doc.issuers()[0], idty.clone());
})?;
}
// Test default filters
let mut filters = IdentitiesFilter::default();
assert!(slice_same_elems(
&mock_identities,
&get_identities(&identities_db, filters, BlockId(5))?
));
// Test by pubkey filter
filters = IdentitiesFilter::by_pubkey(pubkey('A'));
assert_eq!(
vec![mock_identities[0].clone()],
get_identities(&identities_db, filters, BlockId(5))?
);
filters = IdentitiesFilter::by_pubkey(pubkey('C'));
assert_eq!(
vec![mock_identities[2].clone()],
get_identities(&identities_db, filters, BlockId(5))?
);
// Test paging filter with little page size
filters = IdentitiesFilter {
paging: PagingFilter {
from: BlockId(0),
to: None,
page_size: 2,
page_number: 1,
},
by_pubkey: None,
};
assert!(slice_same_elems(
&vec![mock_identities[2].clone(), mock_identities[3].clone()],
&get_identities(&identities_db, filters, BlockId(5))?
));
// Test paging filter with limited interval
filters = IdentitiesFilter {
paging: PagingFilter {
from: BlockId(2),
to: Some(BlockId(3)),
page_size: 50,
page_number: 0,
},
by_pubkey: None,
};
assert_eq!(
vec![mock_identities[2].clone()],
get_identities(&identities_db, filters, BlockId(5))?
);
Ok(())
}
}
...@@ -84,6 +84,20 @@ pub fn receive_req( ...@@ -84,6 +84,20 @@ pub fn receive_req(
), ),
); );
} }
BlockchainRequest::GetIdentities(filters) => {
let identities = durs_blockchain_dal::readers::identity::get_identities(
&bc.wot_databases.identities_db,
filters,
bc.current_blockstamp.id,
)
.expect("Fatal error : get_identities: Fail to read IdentitiesDB !");
responses::sent::send_req_response(
bc,
req_from,
req_id,
&BlockchainResponse::Identities(req_id, identities),
);
}
_ => {} _ => {}
} }
} }
......
...@@ -16,12 +16,13 @@ ...@@ -16,12 +16,13 @@
//! Crypto mocks for projects use dup-crypto //! Crypto mocks for projects use dup-crypto
use dup_crypto::hashs::Hash; use dup_crypto::hashs::Hash;
use dup_crypto::keys::*;
/// Generate mock hash from one character /// Generate mock hash from one character
pub fn hash(character: char) -> Hash { pub fn hash(character: char) -> Hash {
let str_hash: String = (0..64).map(|_| character).collect(); let str_hash: String = (0..64).map(|_| character).collect();
Hash::from_hex(&str_hash).expect("Fail to create hash !") Hash::from_hex(&str_hash).expect("Fail to create mock hash !")
} }
/// Generate mock hash from one byte /// Generate mock hash from one byte
...@@ -33,3 +34,12 @@ pub fn hash_from_byte(byte: u8) -> Hash { ...@@ -33,3 +34,12 @@ pub fn hash_from_byte(byte: u8) -> Hash {
Hash(hash_bin) Hash(hash_bin)
} }
/// Generate mock pubkey from one character
pub fn pubkey(character: char) -> PubKey {
let str_pub: String = (0..44).map(|_| character).collect();
PubKey::Ed25519(
ed25519::PublicKey::from_base58(&str_pub).expect("Fail to create mock pubkey !"),
)
}
// Copyright (C) 2019 Éloïs SANCHEZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Mocks for projects use dubp-documents
use dubp_documents::documents::identity::*;
use dubp_documents::*;
use dup_crypto::hashs::Hash;
use dup_crypto::keys::PubKey;
/// Generate mock identity document
pub fn gen_mock_idty(pubkey: PubKey, created_on: BlockId) -> IdentityDocument {
let idty_builder = IdentityDocumentBuilder {
currency: "",
username: "",
blockstamp: &Blockstamp {
id: created_on,
hash: BlockHash(Hash::default()),
},
issuer: &pubkey,
};
idty_builder.build_with_signature(vec![])
}
...@@ -13,7 +13,9 @@ ...@@ -13,7 +13,9 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Crypto mocks for projects use dubp-documents //! Mocks for projects use dubp-documents
pub mod identity;
use dubp_documents::documents::block::BlockDocument; use dubp_documents::documents::block::BlockDocument;
use dubp_documents::*; use dubp_documents::*;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment