diff --git a/db/src/lib.rs b/db/src/lib.rs index a8f2e073d3f87cdfd84bc170ecb877137c24cead..49a01194e6f43d75dd70d2e8e1cccf096d21e237 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -41,8 +41,8 @@ pub(crate) use duniter_core::dbs::kv_typed::db_schema; pub(crate) use duniter_core::dbs::kv_typed::prelude::*; pub(crate) use duniter_core::dbs::smallvec::SmallVec; pub(crate) use duniter_core::dbs::{ - bincode_db, CorruptedBytes, HashKeyV2, PubKeyKeyV2, SourceAmountValV2, ToDumpString, - WalletConditionsV2, + bincode_db, CorruptedBytes, HashKeyV2, PubKeyKeyV2, PubKeyValV2, SourceAmountValV2, + ToDumpString, WalletConditionsV2, }; pub(crate) use duniter_core::wallet::prelude::*; pub(crate) use duniter_core::wot::WotId; @@ -67,6 +67,7 @@ db_schema!( GvaUtxoIdDbV1, SourceAmountValV2 ], + ["idty_by_username", IdtyByUsername, String, PubKeyValV2], [ "scripts_by_pubkey", ScriptsByPubkey, diff --git a/dbs-reader/src/lib.rs b/dbs-reader/src/lib.rs index 8372372ad4687c247d165b1d98dc9302569add7d..900dec5eb957883e4e35d155e566361b47100cc3 100644 --- a/dbs-reader/src/lib.rs +++ b/dbs-reader/src/lib.rs @@ -159,6 +159,11 @@ pub trait DbsReader { bc_db: &BcV2DbRo<FileBackend>, pubkey: PublicKey, ) -> KvResult<Option<duniter_core::dbs::IdtyDbV2>>; + fn idty_by_username( + &self, + bc_db: &BcV2DbRo<FileBackend>, + username: String, + ) -> KvResult<Option<(PublicKey, duniter_core::dbs::IdtyDbV2)>>; fn peers_and_heads<DB: 'static + NetworkV1DbReadable>( &self, dunp_db: &DB, @@ -353,6 +358,21 @@ impl DbsReader for DbsReaderImpl { self.idty_(bc_db, pubkey) } + fn idty_by_username( + &self, + bc_db: &BcV2DbRo<FileBackend>, + username: String, + ) -> KvResult<Option<(PublicKey, duniter_core::dbs::IdtyDbV2)>> { + self.0 + .idty_by_username() + .get(&username)? + .map_or(Ok(None), |pubkey| match self.idty_(bc_db, pubkey.0) { + Ok(Some(idty)) => Ok(Some((pubkey.0, idty))), + Ok(None) => Ok(None), + Err(e) => Err(e), + }) + } + fn peers_and_heads<DB: 'static + NetworkV1DbReadable>( &self, dunp_db: &DB, diff --git a/gql/src/entities/idty_gva.rs b/gql/src/entities/idty_gva.rs index 8729895ac809679fe4b34fa6966b1e0305fb4a27..4639a76a8241ca9131d303b7b1fec18dbaa2a7a6 100644 --- a/gql/src/entities/idty_gva.rs +++ b/gql/src/entities/idty_gva.rs @@ -13,8 +13,11 @@ // 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(Clone, Debug, async_graphql::SimpleObject)] pub(crate) struct Identity { pub is_member: bool, + pub pubkey: PubKeyGva, pub username: String, } diff --git a/gql/src/queries/idty.rs b/gql/src/queries/idty.rs index 65b43b1b4db4d730e80a34894339f7c6d4920127..cc1cc348b64f850cbbbaca7837a306ecf58b8f31 100644 --- a/gql/src/queries/idty.rs +++ b/gql/src/queries/idty.rs @@ -34,6 +34,27 @@ impl IdtyQuery { .await?? .map(|idty| Identity { is_member: idty.is_member, + pubkey, + username: idty.username, + })) + } + + /// Get identity by username + async fn idty_by_username( + &self, + ctx: &async_graphql::Context<'_>, + #[graphql(desc = "username")] username: String, + ) -> async_graphql::Result<Option<Identity>> { + let data = ctx.data::<GvaSchemaData>()?; + let dbs_reader = data.dbs_reader(); + + Ok(data + .dbs_pool + .execute(move |dbs| dbs_reader.idty_by_username(&dbs.bc_db_ro, username)) + .await?? + .map(|(pubkey, idty)| Identity { + is_member: idty.is_member, + pubkey: PubKeyGva(pubkey), username: idty.username, })) } @@ -79,4 +100,41 @@ mod tests { ); Ok(()) } + + #[tokio::test] + async fn test_idty_by_username() -> anyhow::Result<()> { + let mut dbs_reader = MockDbsReader::new(); + dbs_reader + .expect_idty_by_username() + .withf(|_, s| s == &String::from("JohnDoe")) + .times(1) + .returning(|_, _| { + Ok(Some(( + PublicKey::from_base58("DnjL6hYA1k7FavGHbbir79PKQbmzw63d6bsamBBdUULP") + .expect("wrong pubkey"), + duniter_core::dbs::IdtyDbV2 { + is_member: true, + username: String::from("JohnDoe"), + ..Default::default() + }, + ))) + }); + let schema = create_schema(MockAsyncAccessor::new(), dbs_reader)?; + assert_eq!( + exec_graphql_request( + &schema, + r#"{ idtyByUsername(username: "JohnDoe") {isMember, pubkey} }"# + ) + .await?, + serde_json::json!({ + "data": { + "idtyByUsername": { + "isMember": true, + "pubkey": "DnjL6hYA1k7FavGHbbir79PKQbmzw63d6bsamBBdUULP" + } + } + }) + ); + Ok(()) + } } diff --git a/gql/src/queries/wallets.rs b/gql/src/queries/wallets.rs index aa3636b84419f977de92cff4f243caabd13fda33..2424b8426f9967c9ad4ab5c6acfff975d616d4c2 100644 --- a/gql/src/queries/wallets.rs +++ b/gql/src/queries/wallets.rs @@ -91,6 +91,7 @@ impl WalletsQuery { balance: AmountWithBase::from(pk_with_sa.1), idty: idty_opt.map(|idty_db| Identity { is_member: idty_db.is_member, + pubkey: PubKeyGva(pk_with_sa.0), username: idty_db.username, }), }) @@ -136,10 +137,17 @@ impl WalletsQuery { .map(|WalletWithIdtyOpt(script_with_sa, idty_opt)| Wallet { script: script_with_sa.0.to_string(), balance: AmountWithBase::from(script_with_sa.1), - idty: idty_opt.map(|idty_db| Identity { - is_member: idty_db.is_member, - username: idty_db.username, - }), + idty: if let (Some(idty_db), Some(pubkey)) = + (idty_opt, script_with_sa.0.as_single_sig()) + { + Some(Identity { + is_member: idty_db.is_member, + pubkey: PubKeyGva(pubkey), + username: idty_db.username, + }) + } else { + None + }, }) .collect() }) diff --git a/indexer/src/identities.rs b/indexer/src/identities.rs index 2360c7132ea9978c66b60d0ce669f60f7e232e23..e893e9eca56dc682f6fd00f77adae5af0e3c32c6 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>, + idty_by_username: &mut TxColRw<B::Col, IdtyByUsernameEvent>, ) -> KvResult<()> { let mut identities_pubkeys = HashSet::new(); for idty in block.identities() { @@ -35,6 +36,7 @@ pub(crate) fn update_identities<B: Backend>( wot_id, }, ); + idty_by_username.upsert(idty.username().into(), PubKeyValV2(pubkey)); } for mb in block.joiners() { let pubkey = mb.issuers()[0]; @@ -70,6 +72,7 @@ pub(crate) fn update_identities<B: Backend>( pub(crate) fn revert_identities<B: Backend>( block: &DubpBlockV10, identities: &mut TxColRw<B::Col, GvaIdentitiesEvent>, + idty_by_username: &mut TxColRw<B::Col, IdtyByUsernameEvent>, ) -> KvResult<()> { for mb in block.joiners() { let pubkey = mb.issuers()[0]; @@ -83,6 +86,7 @@ pub(crate) fn revert_identities<B: Backend>( } for idty in block.identities() { let pubkey = idty.issuers()[0]; + idty_by_username.remove(idty.username().into()); identities.remove(PubKeyKeyV2(pubkey)); crate::revert_wot_id(); } diff --git a/indexer/src/lib.rs b/indexer/src/lib.rs index b7fa23aba79fbc4dd9071e7bf5f9275fb439573b..b1451972ef93f679bee4c204dae5a9234ef091f5 100644 --- a/indexer/src/lib.rs +++ b/indexer/src/lib.rs @@ -41,7 +41,7 @@ use duniter_core::{ use duniter_core::{ dbs::{ bincode_db, kv_typed::prelude::*, prelude::*, smallvec::smallvec, FileBackend, HashKeyV2, - PubKeyKeyV2, SourceAmountValV2, WalletConditionsV2, + PubKeyKeyV2, PubKeyValV2, SourceAmountValV2, WalletConditionsV2, }, wot::{WebOfTrust, WotId, MAIN_WOT}, }; @@ -126,7 +126,11 @@ 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.idty_by_username, + )?; if let Some(divident_amount) = block.dividend() { db.blocks_with_ud.upsert(U32BE(blockstamp.number.0), ()); apply_ud::<B>( @@ -153,7 +157,11 @@ 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)?; + identities::revert_identities::<B>( + &block, + &mut db.gva_identities, + &mut db.idty_by_username, + )?; if let Some(divident_amount) = block.dividend() { db.blocks_with_ud.remove(U32BE(block.number().0)); revert_ud::<B>( @@ -331,6 +339,12 @@ mod tests { Some(SourceAmountValV2(SourceAmount::with_base0(1000))) ); assert_eq!(gva_db.txs_by_block().count()?, 0); + assert_eq!( + gva_db.idty_by_username().get(&String::from("elois"))?, + Some(PubKeyValV2(PublicKey::from_base58( + "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx", + )?)) + ); let b1 = DubpBlockV10::from_string_object(&DubpBlockV10Stringified { number: 1, @@ -644,6 +658,10 @@ mod tests { ); assert_eq!(gva_db.balances().get(&WalletConditionsV2(s2))?, None); + revert_block(&b0, currency_params, &gva_db)?; + + assert_eq!(gva_db.idty_by_username().count()?, 0); + Ok(()) } }