diff --git a/Cargo.lock b/Cargo.lock index 3cf5426..d24218d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1025,6 +1025,7 @@ dependencies = [ "lz4_flex", "maplit", "once_cell", + "parking_lot", "resiter", "smallvec", ] diff --git a/db/src/lib.rs b/db/src/lib.rs index 849241d..fca46a2 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -45,6 +45,7 @@ pub(crate) use duniter_core::dbs::{ bincode_db, CorruptedBytes, HashKeyV2, PubKeyKeyV2, SourceAmountValV2, ToDumpString, WalletConditionsV2, }; +pub(crate) use duniter_core::wot::WotId; pub(crate) use serde::{Deserialize, Serialize}; pub(crate) use std::collections::BTreeSet; diff --git a/db/src/values/gva_idty_db.rs b/db/src/values/gva_idty_db.rs index 540deab..13332d9 100644 --- a/db/src/values/gva_idty_db.rs +++ b/db/src/values/gva_idty_db.rs @@ -15,12 +15,13 @@ use crate::*; -#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GvaIdtyDbV1 { pub is_member: bool, pub joins: SmallVec<[BlockNumber; 2]>, pub leaves: BTreeSet, pub first_ud: Option, + pub wot_id: WotId, } impl AsBytes for GvaIdtyDbV1 { diff --git a/dbs-reader/src/uds_of_pubkey.rs b/dbs-reader/src/uds_of_pubkey.rs index e92d341..6e24096 100644 --- a/dbs-reader/src/uds_of_pubkey.rs +++ b/dbs-reader/src/uds_of_pubkey.rs @@ -471,8 +471,8 @@ fn collect_uds>( mod tests { use super::*; - use duniter_core::dbs::smallvec::smallvec as svec; use duniter_core::dbs::{databases::bc_v2::BcV2DbWritable, SourceAmountValV2, UdIdV2}; + use duniter_core::{dbs::smallvec::smallvec as svec, wot::WotId}; use duniter_gva_db::GvaV1DbWritable; #[test] @@ -482,6 +482,7 @@ mod tests { joins: svec![BlockNumber(26), BlockNumber(51)], leaves: [BlockNumber(32)].iter().copied().collect(), first_ud: Some(BlockNumber(29)), + wot_id: WotId(0), }; let blocks_with_ud = vec![ BlockNumber(3), @@ -531,6 +532,7 @@ mod tests { joins: svec![BlockNumber(26), BlockNumber(51)], leaves: [BlockNumber(32)].iter().copied().collect(), first_ud: Some(BlockNumber(29)), + wot_id: WotId(0), }; let bc_db = duniter_core::dbs::databases::bc_v2::BcV2Db::::open(MemConf::default())?; diff --git a/indexer/Cargo.toml b/indexer/Cargo.toml index 25d4999..9e8907a 100644 --- a/indexer/Cargo.toml +++ b/indexer/Cargo.toml @@ -18,12 +18,13 @@ duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core" } duniter-gva-db = { path = "../db" } dubp = { version = "0.54.1", features = ["duniter"] } lz4_flex = { version = "0.7", default-features = false } -once_cell = "1.5.2" +once_cell = "1.7" resiter = "0.4.0" [dev-dependencies] duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core", features = ["mem"] } maplit = "1.0.2" +parking_lot = "0.11" smallvec = { version = "1.4.0", features = ["serde", "write"] } [features] diff --git a/indexer/src/identities.rs b/indexer/src/identities.rs index 0a33b37..2360c71 100644 --- a/indexer/src/identities.rs +++ b/indexer/src/identities.rs @@ -19,13 +19,35 @@ pub(crate) fn update_identities( block: &DubpBlockV10, identities: &mut TxColRw, ) -> KvResult<()> { + let mut identities_pubkeys = HashSet::new(); + for idty in block.identities() { + let pubkey = idty.issuers()[0]; + identities_pubkeys.insert(pubkey); + let wot_id = crate::get_next_wot_id(); + + identities.upsert( + PubKeyKeyV2(pubkey), + GvaIdtyDbV1 { + is_member: true, + joins: smallvec![block.number()], + leaves: BTreeSet::new(), + first_ud: None, + wot_id, + }, + ); + } for mb in block.joiners() { let pubkey = mb.issuers()[0]; - let mut idty = identities.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default(); - idty.is_member = true; - idty.joins.push(block.number()); - identities.upsert(PubKeyKeyV2(pubkey), idty); + // Update identity only if the join event concerns an identity already created in the past + if !identities_pubkeys.contains(&pubkey) { + let mut idty = identities + .get(&PubKeyKeyV2(pubkey))? + .ok_or_else(|| KvError::DbCorrupted("Joiner without identity".to_owned()))?; + idty.is_member = true; + idty.joins.push(block.number()); + identities.upsert(PubKeyKeyV2(pubkey), idty); + } } for revo in block.revoked() { let pubkey = revo.issuer; @@ -52,7 +74,9 @@ pub(crate) fn revert_identities( for mb in block.joiners() { let pubkey = mb.issuers()[0]; - let mut idty = identities.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default(); + let mut idty = identities + .get(&PubKeyKeyV2(pubkey))? + .ok_or_else(|| KvError::DbCorrupted("Joiner without identity".to_owned()))?; idty.is_member = false; idty.joins.pop(); identities.upsert(PubKeyKeyV2(pubkey), idty); @@ -60,6 +84,7 @@ pub(crate) fn revert_identities( for idty in block.identities() { let pubkey = idty.issuers()[0]; identities.remove(PubKeyKeyV2(pubkey)); + crate::revert_wot_id(); } for revo in block.revoked() { let pubkey = revo.issuer; diff --git a/indexer/src/lib.rs b/indexer/src/lib.rs index 07f8ca9..3f03199 100644 --- a/indexer/src/lib.rs +++ b/indexer/src/lib.rs @@ -36,21 +36,24 @@ use dubp::{ }, wallet::prelude::*, }; -use duniter_core::dbs::{ - bincode_db, kv_typed::prelude::*, prelude::*, FileBackend, HashKeyV2, PubKeyKeyV2, - SourceAmountValV2, WalletConditionsV2, +use duniter_core::{ + dbs::{ + bincode_db, kv_typed::prelude::*, prelude::*, smallvec::smallvec, FileBackend, HashKeyV2, + PubKeyKeyV2, SourceAmountValV2, WalletConditionsV2, + }, + wot::{WebOfTrust, WotId, MAIN_WOT}, }; use duniter_gva_db::*; +use once_cell::sync::OnceCell; use resiter::filter::Filter; use std::{ - collections::{BTreeSet, HashMap}, + collections::{BTreeSet, HashMap, HashSet}, + ops::AddAssign, path::Path, }; -static GVA_DB_RO: once_cell::sync::OnceCell> = - once_cell::sync::OnceCell::new(); -static GVA_DB_RW: once_cell::sync::OnceCell> = - once_cell::sync::OnceCell::new(); +static GVA_DB_RO: OnceCell> = OnceCell::new(); +static GVA_DB_RW: OnceCell> = OnceCell::new(); pub fn get_gva_db_ro(profile_path_opt: Option<&Path>) -> &'static GvaV1DbRo { GVA_DB_RO.get_or_init(|| get_gva_db_rw(profile_path_opt).get_ro_handler()) @@ -65,6 +68,39 @@ pub fn get_gva_db_rw(profile_path_opt: Option<&Path>) -> &'static GvaV1Db = None; + +fn get_next_wot_id() -> WotId { + WotId(unsafe { + if let Some(ref mut next_wot_id_) = NEXT_WOT_ID { + let next_wot_id = *next_wot_id_; + next_wot_id_.add_assign(1); + next_wot_id + } else { + NEXT_WOT_ID = Some(if let Some(main_wot) = MAIN_WOT.get() { + main_wot.read().size() + } else { + 0 + }); + 0 + } + }) +} +fn revert_wot_id() { + unsafe { + if let Some(ref mut next_wot_id_) = NEXT_WOT_ID { + use std::ops::SubAssign as _; + next_wot_id_.sub_assign(1); + } else { + NEXT_WOT_ID = Some(if let Some(main_wot) = MAIN_WOT.get() { + main_wot.read().size() - 1 + } else { + unreachable!() + }); + } + }; +} + pub struct UtxoV10<'s> { pub id: UtxoIdV10, pub amount: SourceAmount, @@ -256,9 +292,13 @@ mod tests { documents::transaction::TransactionDocumentV10Stringified, documents_parser::prelude::FromStringObject, }; + use once_cell::sync::Lazy; + + static TESTS_MUTEX: Lazy> = Lazy::new(|| parking_lot::Mutex::new(())); #[test] fn test_gva_apply_block() -> anyhow::Result<()> { + let _ = TESTS_MUTEX.lock(); let gva_db = GvaV1Db::::open(MemConf::default())?; let s1 = WalletScriptV10::single_sig(PublicKey::from_base58( @@ -272,6 +312,7 @@ mod tests { version: 10, median_time: 5_243, dividend: Some(1000), + identities: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:Ydnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()], joiners: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:FFeyrvYio9uYwY5aMcDGswZPNjGLrl8THn9l3EPKSNySD3SDSHjCljSfFEwb87sroyzJQoVzPwER0sW/cbZMDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()], inner_hash: Some("0000000A65A12DB95B3153BCD05DB4D5C30CC7F0B1292D9FFBC3DE67F72F6040".to_owned()), signature: "7B0hvcfajE2G8nBLp0vLVaQcQdQIyli21Gu8F2l+nimKHRe+fUNi+MWd1e/u29BYZa+RZ1yxhbHIbFzytg7fAA==".to_owned(), @@ -430,6 +471,7 @@ mod tests { #[test] fn test_gva_revert_block() -> anyhow::Result<()> { + let _ = TESTS_MUTEX.lock(); let gva_db = GvaV1Db::::open(MemConf::default())?; let s1 = WalletScriptV10::single_sig(PublicKey::from_base58( @@ -443,6 +485,7 @@ mod tests { version: 10, median_time: 5_243, dividend: Some(1000), + identities: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:Ydnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()], joiners: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:FFeyrvYio9uYwY5aMcDGswZPNjGLrl8THn9l3EPKSNySD3SDSHjCljSfFEwb87sroyzJQoVzPwER0sW/cbZMDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()], inner_hash: Some("0000000A65A12DB95B3153BCD05DB4D5C30CC7F0B1292D9FFBC3DE67F72F6040".to_owned()), signature: "7B0hvcfajE2G8nBLp0vLVaQcQdQIyli21Gu8F2l+nimKHRe+fUNi+MWd1e/u29BYZa+RZ1yxhbHIbFzytg7fAA==".to_owned(),