diff --git a/db/src/keys.rs b/db/src/keys.rs index ced890746bed7f72d910bf66d2e3e0f26c3ea091..866dc6b0348018d28711578f840927a9baa30678 100644 --- a/db/src/keys.rs +++ b/db/src/keys.rs @@ -13,5 +13,6 @@ // 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 cert_id; pub mod gva_utxo_id; pub mod wallet_hash_with_bn; diff --git a/db/src/keys/cert_id.rs b/db/src/keys/cert_id.rs new file mode 100644 index 0000000000000000000000000000000000000000..fa2b255621b4a57ca2e8842710b01c8d64dd376a --- /dev/null +++ b/db/src/keys/cert_id.rs @@ -0,0 +1,66 @@ +// Copyright (C) 2021 Pascal Engélibert +// +// 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/>. + +use crate::*; + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct GvaCertIdKeyDbV1 { + pub issuer: U64BE, + pub target: U64BE, +} + +impl Default for GvaCertIdKeyDbV1 { + fn default() -> Self { + Self { + issuer: U64BE(0), + target: U64BE(0), + } + } +} + +impl std::fmt::Display for GvaCertIdKeyDbV1 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.issuer.0, self.target.0,) + } +} + +impl AsBytes for GvaCertIdKeyDbV1 { + fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T { + f(&([self.issuer.0.to_be_bytes(), self.target.0.to_be_bytes()].concat())) + } +} + +impl FromBytes for GvaCertIdKeyDbV1 { + type Err = CorruptedBytes; + + fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { + Ok(Self { + issuer: U64BE::from_bytes(&bytes[0..8]) + .map_err(|_| CorruptedBytes("db corrupted".to_owned()))?, + target: U64BE::from_bytes(&bytes[8..16]) + .map_err(|_| CorruptedBytes("db corrupted".to_owned()))?, + }) + } +} + +#[cfg(feature = "explorer")] +impl ExplorableKey for GvaCertIdKeyDbV1 { + fn from_explorer_str(_: &str) -> std::result::Result<Self, FromExplorerKeyErr> { + unimplemented!() + } + fn to_explorer_string(&self) -> KvResult<String> { + Ok(self.to_string()) + } +} diff --git a/db/src/lib.rs b/db/src/lib.rs index 5c64cd70ec1bdcb6072fb31d9ce2a8292f827489..6df6423ea592eb516ab5b4f1cb299d783068eed4 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -25,17 +25,19 @@ mod keys; mod values; +pub use keys::cert_id::GvaCertIdKeyDbV1; pub use keys::gva_utxo_id::GvaUtxoIdDbV1; pub use keys::wallet_hash_with_bn::WalletHashWithBnV1Db; pub use values::gva_block_db::GvaBlockDbV1; +pub use values::gva_cert_db::{GvaCertDbV1, GvaCertIdDbV1, GvaExpiredCertDbV1}; pub use values::gva_idty_db::GvaIdtyDbV1; pub use values::gva_tx::GvaTxDbV1; pub use values::wallet_script_array::WalletScriptArrayV2; -pub use values::HashDb; +pub use values::{HashDb, PublicKeyDb}; pub(crate) use bincode::Options as _; pub(crate) use duniter_core::common::prelude::*; -pub(crate) use duniter_core::crypto::hashs::Hash; +pub(crate) use duniter_core::crypto::{hashs::Hash, keys::ed25519::PublicKey}; pub(crate) use duniter_core::dbs::kv_typed; pub(crate) use duniter_core::dbs::kv_typed::db_schema; pub(crate) use duniter_core::dbs::kv_typed::prelude::*; @@ -57,7 +59,10 @@ db_schema!( ["blocks_with_ud", BlocksWithUd, U32BE, ()], ["blockchain_time", BlockchainTime, U32BE, u64], ["blocks_chunk_hash", BlocksChunkHash, U32BE, HashDb], + ["certifications", Certifications, GvaCertIdKeyDbV1, GvaCertDbV1], + ["certs_by_expire", CertsByExpire, U64BE, Vec<GvaCertIdDbV1>], ["current_blocks_chunk", CurrentBlocksChunk, U32BE, GvaBlockDbV1], + ["expired_certs", ExpiredCerts, U32BE, Vec<GvaExpiredCertDbV1>], ["gva_identities", GvaIdentities, PubKeyKeyV2, GvaIdtyDbV1], [ "gva_utxos", @@ -65,6 +70,7 @@ db_schema!( GvaUtxoIdDbV1, SourceAmountValV2 ], + ["idties_by_id", IdtiesById, U64BE, PublicKeyDb], [ "scripts_by_pubkey", ScriptsByPubkey, diff --git a/db/src/values.rs b/db/src/values.rs index 8dbd3baab691823e99536f44345773b2d364e47e..df7b16036c792e3aa766f915bfaf406b53c2365c 100644 --- a/db/src/values.rs +++ b/db/src/values.rs @@ -16,6 +16,7 @@ use crate::*; pub mod gva_block_db; +pub mod gva_cert_db; pub mod gva_idty_db; pub mod gva_tx; pub mod wallet_script_array; @@ -56,3 +57,40 @@ impl ExplorableValue for HashDb { Ok(serde_json::Value::String(self.0.to_hex())) } } + +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct PublicKeyDb(pub PublicKey); + +impl AsBytes for PublicKeyDb { + fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T { + f(self.0.as_ref()) + } +} + +impl kv_typed::prelude::FromBytes for PublicKeyDb { + type Err = bincode::Error; + + fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { + let mut pubkey_bytes = [0; 32]; + pubkey_bytes.copy_from_slice(bytes); + Ok(Self(PublicKey::from_32_bytes_array(pubkey_bytes))) + } +} + +impl ToDumpString for PublicKeyDb { + fn to_dump_string(&self) -> String { + todo!() + } +} + +#[cfg(feature = "explorer")] +impl ExplorableValue for PublicKeyDb { + fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> { + Ok(Self( + PublicKey::from_hex(source).map_err(|e| FromExplorerValueErr(e.into()))?, + )) + } + fn to_explorer_json(&self) -> KvResult<serde_json::Value> { + Ok(serde_json::Value::String(self.0.to_hex())) + } +} diff --git a/db/src/values/gva_cert_db.rs b/db/src/values/gva_cert_db.rs new file mode 100644 index 0000000000000000000000000000000000000000..5e10778e5e2d9090d67b063a9c227d130f5639b8 --- /dev/null +++ b/db/src/values/gva_cert_db.rs @@ -0,0 +1,174 @@ +// Copyright (C) 2021 Pascal Engélibert +// +// 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/>. + +use crate::*; + +// TODO remove repr(packed) +#[derive( + Copy, + Clone, + Debug, + Default, + PartialEq, + Serialize, + Deserialize, + zerocopy::AsBytes, + zerocopy::FromBytes, +)] +#[repr(packed)] +pub struct GvaCertIdDbV1 { + pub issuer: usize, + pub target: usize, +} + +impl GvaCertIdDbV1 { + pub fn to_key(self) -> GvaCertIdKeyDbV1 { + GvaCertIdKeyDbV1 { + issuer: U64BE(self.issuer as u64), + target: U64BE(self.target as u64), + } + } +} + +impl AsBytes for GvaCertIdDbV1 { + fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T { + f(&bincode_db() + .serialize(&self) + .unwrap_or_else(|_| unreachable!())) + } +} + +impl kv_typed::prelude::FromBytes for GvaCertIdDbV1 { + type Err = bincode::Error; + + fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { + bincode_db().deserialize(bytes) + } +} + +impl ToDumpString for GvaCertIdDbV1 { + fn to_dump_string(&self) -> String { + todo!() + } +} + +#[cfg(feature = "explorer")] +impl ExplorableValue for GvaCertIdDbV1 { + fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> { + serde_json::from_str(source).map_err(|e| FromExplorerValueErr(e.into())) + } + fn to_explorer_json(&self) -> KvResult<serde_json::Value> { + serde_json::to_value(&self).map_err(|e| KvError::DeserError(e.into())) + } +} + +// TODO remove repr(packed) +#[derive( + Copy, + Clone, + Debug, + Default, + PartialEq, + Serialize, + Deserialize, + zerocopy::AsBytes, + zerocopy::FromBytes, +)] +#[repr(packed)] +pub struct GvaCertDbV1 { + pub written_on: BlockNumber, +} + +impl AsBytes for GvaCertDbV1 { + fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T { + f(&bincode_db() + .serialize(&self) + .unwrap_or_else(|_| unreachable!())) + } +} + +impl kv_typed::prelude::FromBytes for GvaCertDbV1 { + type Err = bincode::Error; + + fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { + bincode_db().deserialize(bytes) + } +} + +impl ToDumpString for GvaCertDbV1 { + fn to_dump_string(&self) -> String { + todo!() + } +} + +#[cfg(feature = "explorer")] +impl ExplorableValue for GvaCertDbV1 { + fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> { + serde_json::from_str(source).map_err(|e| FromExplorerValueErr(e.into())) + } + fn to_explorer_json(&self) -> KvResult<serde_json::Value> { + serde_json::to_value(&self).map_err(|e| KvError::DeserError(e.into())) + } +} + +// TODO remove repr(packed) +#[derive( + Copy, + Clone, + Debug, + Default, + PartialEq, + Serialize, + Deserialize, + zerocopy::AsBytes, + zerocopy::FromBytes, +)] +#[repr(packed)] +pub struct GvaExpiredCertDbV1 { + pub id: GvaCertIdDbV1, + pub cert: GvaCertDbV1, +} + +impl AsBytes for GvaExpiredCertDbV1 { + fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T { + f(&bincode_db() + .serialize(&self) + .unwrap_or_else(|_| unreachable!())) + } +} + +impl kv_typed::prelude::FromBytes for GvaExpiredCertDbV1 { + type Err = bincode::Error; + + fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { + bincode_db().deserialize(bytes) + } +} + +impl ToDumpString for GvaExpiredCertDbV1 { + fn to_dump_string(&self) -> String { + todo!() + } +} + +#[cfg(feature = "explorer")] +impl ExplorableValue for GvaExpiredCertDbV1 { + fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> { + serde_json::from_str(source).map_err(|e| FromExplorerValueErr(e.into())) + } + fn to_explorer_json(&self) -> KvResult<serde_json::Value> { + serde_json::to_value(&self).map_err(|e| KvError::DeserError(e.into())) + } +} diff --git a/db/src/values/gva_idty_db.rs b/db/src/values/gva_idty_db.rs index 13332d92d16a119d13f7fdf51898aab13a25c2f9..add9b5e6f435768613ca79b025b66244a31a909a 100644 --- a/db/src/values/gva_idty_db.rs +++ b/db/src/values/gva_idty_db.rs @@ -14,6 +14,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::*; +use std::collections::HashSet; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct GvaIdtyDbV1 { @@ -22,6 +23,7 @@ pub struct GvaIdtyDbV1 { pub leaves: BTreeSet<BlockNumber>, pub first_ud: Option<BlockNumber>, pub wot_id: WotId, + pub certifiers: HashSet<WotId>, } impl AsBytes for GvaIdtyDbV1 { diff --git a/dbs-reader/src/uds_of_pubkey.rs b/dbs-reader/src/uds_of_pubkey.rs index 6e240966a71b8842e8ec10eda5a4d0b9c39cc85d..a132deb78883de9b8897064b8135f8a37742fcbc 100644 --- a/dbs-reader/src/uds_of_pubkey.rs +++ b/dbs-reader/src/uds_of_pubkey.rs @@ -474,6 +474,7 @@ mod tests { 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; + use std::collections::HashSet; #[test] fn test_filter_blocks_numbers() -> KvResult<()> { @@ -483,6 +484,7 @@ mod tests { leaves: [BlockNumber(32)].iter().copied().collect(), first_ud: Some(BlockNumber(29)), wot_id: WotId(0), + certifiers: HashSet::new(), }; let blocks_with_ud = vec![ BlockNumber(3), @@ -533,6 +535,7 @@ mod tests { leaves: [BlockNumber(32)].iter().copied().collect(), first_ud: Some(BlockNumber(29)), wot_id: WotId(0), + certifiers: HashSet::new(), }; let bc_db = duniter_core::dbs::databases::bc_v2::BcV2Db::<Mem>::open(MemConf::default())?; diff --git a/indexer/src/certifications.rs b/indexer/src/certifications.rs new file mode 100644 index 0000000000000000000000000000000000000000..5a10c47a5c4846411c0332f624a0119e59fc6d44 --- /dev/null +++ b/indexer/src/certifications.rs @@ -0,0 +1,438 @@ +// Copyright (C) 2021 Pascal Engélibert +// +// 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/>. + +use crate::*; + +pub(crate) fn update_certifications<B: Backend>( + block: &DubpBlockV10, + gva_db: &mut GvaV1DbTxRw<B::Col>, +) -> KvResult<()> { + let mut expiring_certs = Vec::new(); + gva_db + .certs_by_expire + .iter(U64BE(0)..U64BE(block.common_time()), |it| { + for row in it { + if let Ok((_, cert_ids)) = row { + for cert_id in cert_ids { + // TODO error handling + if let Ok(Some(cert)) = gva_db.certifications.get(&cert_id.to_key()) { + expiring_certs.push(GvaExpiredCertDbV1 { id: cert_id, cert }); + } else { + // TODO what if certification doesn't exist? + } + } + } + } + }); + for cert in expiring_certs.iter() { + gva_db.certifications.remove(cert.id.to_key()); + if let Some(target_pubkey) = gva_db.idties_by_id.get(&U64BE(cert.id.target as u64))? { + if let Some(mut target_idty) = + gva_db.gva_identities.get(&PubKeyKeyV2(target_pubkey.0))? + { + target_idty.certifiers.remove(&WotId(cert.id.issuer)); + gva_db + .gva_identities + .upsert(PubKeyKeyV2(target_pubkey.0), target_idty); + } + } + } + if !expiring_certs.is_empty() { + gva_db + .expired_certs + .upsert(U32BE(block.number().0), expiring_certs); + } + + for cert in block.certifications() { + if let (Some(issuer_idty), Some(target_idty)) = ( + gva_db.gva_identities.get(&PubKeyKeyV2(cert.issuer))?, + gva_db.gva_identities.get(&PubKeyKeyV2(cert.target))?, + ) { + let cert_id = GvaCertIdDbV1 { + issuer: issuer_idty.wot_id.0, + target: target_idty.wot_id.0, + }; + if gva_db.certifications.get(&cert_id.to_key())?.is_none() + // TODO !contains_key + { + let mut target_idty = target_idty.clone(); + target_idty.certifiers.insert(issuer_idty.wot_id); + gva_db + .gva_identities + .upsert(PubKeyKeyV2(cert.target), target_idty); + } + gva_db.certifications.upsert( + cert_id.to_key(), + GvaCertDbV1 { + written_on: block.number(), + }, + ); + + // TODO sig_validity + if let Some(created_time) = gva_db.blockchain_time.get(&U32BE(cert.block_number.0))? { + let mut certs_same_expire = gva_db + .certs_by_expire + .get(&U64BE(created_time + 42))? + .unwrap_or_else(Vec::new); + certs_same_expire.push(cert_id); + gva_db + .certs_by_expire + .upsert(U64BE(created_time + 42), certs_same_expire); + } else { + // TODO what if the block don't exist? + } + } else { + // TODO what if idties don't exist? + } + } + + Ok(()) +} + +pub(crate) fn revert_certifications<B: Backend>( + _block: &DubpBlockV10, + _gva_db: &mut GvaV1DbTxRw<B::Col>, +) -> KvResult<()> { + // TODO + Ok(()) +} + +#[cfg(test)] +mod tests { + + use super::super::identities::update_identities; + use super::*; + use duniter_core::{ + crypto::{ + bases::b58::ToBase58, + keys::{ed25519::Ed25519KeyPair, KeyPair}, + }, + documents_parser::prelude::FromStringObject, + }; + use maplit::hashset; + + #[test] + fn test_update_certifications() -> KvResult<()> { + let u1_pubkey = Ed25519KeyPair::generate_random().unwrap().public_key(); + let u2_pubkey = Ed25519KeyPair::generate_random().unwrap().public_key(); + let u3_pubkey = Ed25519KeyPair::generate_random().unwrap().public_key(); + let u4_pubkey = Ed25519KeyPair::generate_random().unwrap().public_key(); + let u1_b58 = u1_pubkey.to_base58(); + let u2_b58 = u2_pubkey.to_base58(); + let u3_b58 = u3_pubkey.to_base58(); + let u4_b58 = u4_pubkey.to_base58(); + + let gva_db = GvaV1Db::<Mem>::open(MemConf::default())?; + + let b0 = DubpBlockV10::from_string_object(&DubpBlockV10Stringified { + number: 0, + median_time: 1000, + identities: vec![ + format!("{}:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:0-0000000000000000000000000000000000000000000000000000000000000000:u1", u1_b58), + format!("{}:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:0-0000000000000000000000000000000000000000000000000000000000000000:u2", u2_b58), + format!("{}:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:0-0000000000000000000000000000000000000000000000000000000000000000:u3", u3_b58), + ], + joiners: vec![ + format!("{}:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:0-0000000000000000000000000000000000000000000000000000000000000000:0-0000000000000000000000000000000000000000000000000000000000000000:u1", u1_b58), + format!("{}:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:0-0000000000000000000000000000000000000000000000000000000000000000:0-0000000000000000000000000000000000000000000000000000000000000000:u2", u2_b58), + format!("{}:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:0-0000000000000000000000000000000000000000000000000000000000000000:0-0000000000000000000000000000000000000000000000000000000000000000:u3", u3_b58), + ], + certifications: vec![ + format!("{}:{}:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", u1_b58, u2_b58), + format!("{}:{}:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", u2_b58, u1_b58), + format!("{}:{}:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", u1_b58, u3_b58), + ], + inner_hash: Some("0000000000000000000000000000000000000000000000000000000000000000".into()), + signature: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==".into(), + hash: Some("0000000000000000000000000000000000000000000000000000000000000000".into()), + ..Default::default() + }).unwrap(); + + (&gva_db).write(|mut db| { + db.blockchain_time.upsert(U32BE(0), b0.common_time()); + update_identities::<Mem>(&b0, &mut db.gva_identities, &mut db.idties_by_id)?; + update_certifications::<Mem>(&b0, &mut db) + })?; + + assert_eq!(gva_db.certifications().count()?, 3); + assert_eq!( + gva_db.certifications().get(&GvaCertIdKeyDbV1 { + issuer: U64BE(0), + target: U64BE(1) + })?, + Some(GvaCertDbV1 { + written_on: BlockNumber(0), + }) + ); + assert_eq!( + gva_db.certifications().get(&GvaCertIdKeyDbV1 { + issuer: U64BE(1), + target: U64BE(0) + })?, + Some(GvaCertDbV1 { + written_on: BlockNumber(0), + }) + ); + assert_eq!( + gva_db.certifications().get(&GvaCertIdKeyDbV1 { + issuer: U64BE(0), + target: U64BE(2) + })?, + Some(GvaCertDbV1 { + written_on: BlockNumber(0), + }) + ); + assert_eq!(gva_db.certs_by_expire().count()?, 1); + assert_eq!( + gva_db + .certs_by_expire() + .get(&U64BE(b0.common_time() + 42))?, + Some(vec![ + GvaCertIdDbV1 { + issuer: 0, + target: 1 + }, + GvaCertIdDbV1 { + issuer: 1, + target: 0 + }, + GvaCertIdDbV1 { + issuer: 0, + target: 2 + } + ]) + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u1_pubkey))? + .unwrap() + .certifiers, + hashset! {WotId(1)} + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u2_pubkey))? + .unwrap() + .certifiers, + hashset! {WotId(0)} + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u3_pubkey))? + .unwrap() + .certifiers, + hashset! {WotId(0)} + ); + assert_eq!(gva_db.expired_certs().count()?, 0); + + let b1 = DubpBlockV10::from_string_object(&DubpBlockV10Stringified { + number: 1, + median_time: 1030, + identities: vec![ + format!("{}:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:0-0000000000000000000000000000000000000000000000000000000000000000:u4", u4_b58), + ], + joiners: vec![ + format!("{}:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==:0-0000000000000000000000000000000000000000000000000000000000000000:0-0000000000000000000000000000000000000000000000000000000000000000:u4", u4_b58), + ], + certifications: vec![ + format!("{}:{}:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", u1_b58, u4_b58), + format!("{}:{}:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", u2_b58, u4_b58), + format!("{}:{}:0:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", u3_b58, u2_b58), + ], + inner_hash: Some("0000000000000000000000000000000000000000000000000000000000000001".into()), + signature: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==".into(), + hash: Some("0000000000000000000000000000000000000000000000000000000000000001".into()), + ..Default::default() + }).unwrap(); + + (&gva_db).write(|mut db| { + db.blockchain_time.upsert(U32BE(1), b1.common_time()); + update_identities::<Mem>(&b1, &mut db.gva_identities, &mut db.idties_by_id)?; + update_certifications::<Mem>(&b1, &mut db) + })?; + + assert_eq!(gva_db.certifications().count()?, 6); + assert_eq!( + gva_db.certifications().get(&GvaCertIdKeyDbV1 { + issuer: U64BE(0), + target: U64BE(3) + })?, + Some(GvaCertDbV1 { + written_on: BlockNumber(1), + }) + ); + assert_eq!( + gva_db.certifications().get(&GvaCertIdKeyDbV1 { + issuer: U64BE(1), + target: U64BE(3) + })?, + Some(GvaCertDbV1 { + written_on: BlockNumber(1), + }) + ); + assert_eq!( + gva_db.certifications().get(&GvaCertIdKeyDbV1 { + issuer: U64BE(2), + target: U64BE(1) + })?, + Some(GvaCertDbV1 { + written_on: BlockNumber(1), + }) + ); + assert_eq!(gva_db.certs_by_expire().count()?, 1); + assert_eq!( + gva_db + .certs_by_expire() + .get(&U64BE(b0.common_time() + 42))?, + Some(vec![ + GvaCertIdDbV1 { + issuer: 0, + target: 1 + }, + GvaCertIdDbV1 { + issuer: 1, + target: 0 + }, + GvaCertIdDbV1 { + issuer: 0, + target: 2 + }, + GvaCertIdDbV1 { + issuer: 0, + target: 3 + }, + GvaCertIdDbV1 { + issuer: 1, + target: 3 + }, + GvaCertIdDbV1 { + issuer: 2, + target: 1 + } + ]) + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u1_pubkey))? + .unwrap() + .certifiers, + hashset! {WotId(1)} + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u2_pubkey))? + .unwrap() + .certifiers, + hashset! {WotId(0), WotId(2)} + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u3_pubkey))? + .unwrap() + .certifiers, + hashset! {WotId(0)} + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u4_pubkey))? + .unwrap() + .certifiers, + hashset! {WotId(0), WotId(1)} + ); + assert_eq!(gva_db.expired_certs().count()?, 0); + + let b2 = DubpBlockV10::from_string_object(&DubpBlockV10Stringified { + number: 2, + median_time: 1060, + joiners: vec![], + certifications: vec![ + format!("{}:{}:1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", u1_b58, u2_b58), + ], + inner_hash: Some("0000000000000000000000000000000000000000000000000000000000000002".into()), + signature: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==".into(), + hash: Some("0000000000000000000000000000000000000000000000000000000000000002".into()), + ..Default::default() + }).unwrap(); + + (&gva_db).write(|mut db| { + db.blockchain_time.upsert(U32BE(2), b2.common_time()); + update_identities::<Mem>(&b2, &mut db.gva_identities, &mut db.idties_by_id)?; + update_certifications::<Mem>(&b2, &mut db) + })?; + + assert_eq!(gva_db.certifications().count()?, 1); + assert_eq!( + gva_db.certifications().get(&GvaCertIdKeyDbV1 { + issuer: U64BE(0), + target: U64BE(1) + })?, + Some(GvaCertDbV1 { + written_on: BlockNumber(2), + }) + ); + assert_eq!(gva_db.certs_by_expire().count()?, 1); + assert_eq!( + gva_db + .certs_by_expire() + .get(&U64BE(b1.common_time() + 42))?, + Some(vec![GvaCertIdDbV1 { + issuer: 0, + target: 1 + },]) + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u1_pubkey))? + .unwrap() + .certifiers, + hashset! {} + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u2_pubkey))? + .unwrap() + .certifiers, + hashset! {WotId(0)} + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u3_pubkey))? + .unwrap() + .certifiers, + hashset! {} + ); + assert_eq!( + gva_db + .gva_identities() + .get(&PubKeyKeyV2(u4_pubkey))? + .unwrap() + .certifiers, + hashset! {} + ); + assert_eq!(gva_db.expired_certs().count()?, 5); + + Ok(()) + } +} diff --git a/indexer/src/identities.rs b/indexer/src/identities.rs index 2360c7132ea9978c66b60d0ce669f60f7e232e23..787ff3a07185545103497f17ef2e44bdd1086c6b 100644 --- a/indexer/src/identities.rs +++ b/indexer/src/identities.rs @@ -18,6 +18,7 @@ use crate::*; pub(crate) fn update_identities<B: Backend>( block: &DubpBlockV10, identities: &mut TxColRw<B::Col, GvaIdentitiesEvent>, + idties_by_id: &mut TxColRw<B::Col, IdtiesByIdEvent>, ) -> KvResult<()> { let mut identities_pubkeys = HashSet::new(); for idty in block.identities() { @@ -33,8 +34,10 @@ pub(crate) fn update_identities<B: Backend>( leaves: BTreeSet::new(), first_ud: None, wot_id, + certifiers: HashSet::new(), }, ); + idties_by_id.upsert(U64BE(wot_id.0 as u64), PublicKeyDb(pubkey)) } for mb in block.joiners() { let pubkey = mb.issuers()[0]; @@ -70,6 +73,7 @@ pub(crate) fn update_identities<B: Backend>( pub(crate) fn revert_identities<B: Backend>( block: &DubpBlockV10, identities: &mut TxColRw<B::Col, GvaIdentitiesEvent>, + idties_by_id: &mut TxColRw<B::Col, IdtiesByIdEvent>, ) -> KvResult<()> { for mb in block.joiners() { let pubkey = mb.issuers()[0]; @@ -83,6 +87,9 @@ pub(crate) fn revert_identities<B: Backend>( } for idty in block.identities() { let pubkey = idty.issuers()[0]; + if let Some(idty) = identities.get(&PubKeyKeyV2(pubkey))? { + idties_by_id.remove(U64BE(idty.wot_id.0 as u64)); + } identities.remove(PubKeyKeyV2(pubkey)); crate::revert_wot_id(); } diff --git a/indexer/src/lib.rs b/indexer/src/lib.rs index 87cb8c20f90a1188ce79c7c5fc0e0d9404fb0194..f4adb59b962721eb8ea0396d506a030e96827001 100644 --- a/indexer/src/lib.rs +++ b/indexer/src/lib.rs @@ -23,6 +23,7 @@ )] mod blocks_chunks; +mod certifications; mod identities; mod tx; mod utxos; @@ -81,9 +82,10 @@ fn get_next_wot_id() -> WotId { next_wot_id } else { NEXT_WOT_ID = Some(if let Some(main_wot) = MAIN_WOT.get() { - main_wot.read().size() + main_wot.read().size() + 1 + // TODO ne faudrait-il pas dans ce cas renvoyer `main_wot.read().size()` ? } else { - 0 + 1 }); 0 } @@ -125,7 +127,8 @@ pub fn apply_block<B: Backend>( .upsert(U64BE(block.common_time()), block.number().0); db.blockchain_time .upsert(U32BE(block.number().0), block.common_time()); - identities::update_identities::<B>(&block, &mut db.gva_identities)?; + identities::update_identities::<B>(&block, &mut db.gva_identities, &mut db.idties_by_id)?; + certifications::update_certifications::<B>(&block, &mut db)?; if let Some(divident_amount) = block.dividend() { db.blocks_with_ud.upsert(U32BE(blockstamp.number.0), ()); apply_ud::<B>( @@ -152,7 +155,8 @@ pub fn revert_block<B: Backend>( gva_db.write(|mut db| { db.blocks_by_common_time.remove(U64BE(block.common_time())); db.blockchain_time.remove(U32BE(block.number().0)); - identities::revert_identities::<B>(&block, &mut db.gva_identities)?; + certifications::revert_certifications::<B>(&block, &mut db)?; + identities::revert_identities::<B>(&block, &mut db.gva_identities, &mut db.idties_by_id)?; if let Some(divident_amount) = block.dividend() { db.blocks_with_ud.remove(U32BE(block.number().0)); revert_ud::<B>(