feat(gql): add query wallets

This commit is contained in:
librelois 2021-06-03 20:43:22 +02:00
parent a09ef43775
commit 2f73e4bce0
12 changed files with 705 additions and 11 deletions

1
Cargo.lock generated
View file

@ -984,6 +984,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"arrayvec 0.7.0",
"bincode",
"duniter-core",
"duniter-gva-db",
"flate2",

View file

@ -17,6 +17,7 @@ mock = ["mockall"]
[dependencies]
anyhow = "1.0.34"
arrayvec = { version = "0.7", features = ["serde"] }
bincode = "1.3"
duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core" }
duniter-gva-db = { path = "../db" }
flate2 = { version = "1.0", features = ["zlib-ng-compat"], default-features = false }

83
dbs-reader/src/cursors.rs Normal file
View file

@ -0,0 +1,83 @@
// Copyright (C) 2020 É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/>.
use crate::*;
use duniter_core::crypto::keys::ed25519::PublicKey;
use duniter_core::crypto::keys::PublicKey as _;
use duniter_core::dbs::WalletConditionsV2;
#[derive(Clone, Copy, Debug)]
pub struct WrongCursor;
impl std::fmt::Display for WrongCursor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "wrong cursor")
}
}
impl std::error::Error for WrongCursor {}
pub trait Cursor:
'static + Clone + std::fmt::Debug + std::fmt::Display + Default + FromStr + Ord
{
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PubKeyCursor(pub PublicKey);
impl PubKeyCursor {
pub fn from_ref(pk: &PublicKey) -> &Self {
#[allow(trivial_casts)]
unsafe {
&*(pk as *const PublicKey as *const PubKeyCursor)
}
}
}
impl Cursor for PubKeyCursor {}
impl std::fmt::Display for PubKeyCursor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.to_string())
}
}
impl FromStr for PubKeyCursor {
type Err = WrongCursor;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(pk) = PublicKey::from_base58(s) {
Ok(PubKeyCursor(pk))
} else {
Err(WrongCursor)
}
}
}
impl From<PubKeyCursor> for WalletConditionsV2 {
fn from(val: PubKeyCursor) -> Self {
WalletConditionsV2(WalletScriptV10::single_sig(val.0))
}
}
impl Ord for PubKeyCursor {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.as_ref().cmp(other.0.as_ref())
}
}
impl PartialOrd for PubKeyCursor {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.0.as_ref().partial_cmp(other.0.as_ref())
}
}

View file

@ -25,6 +25,7 @@
pub mod block;
pub mod blocks_chunks;
pub mod current_frame;
pub mod cursors;
pub mod find_inputs;
pub mod idty;
pub mod network;
@ -32,7 +33,9 @@ pub mod pagination;
pub mod txs_history;
pub mod uds_of_pubkey;
pub mod utxos;
pub mod wallets;
pub use crate::cursors::{Cursor, PubKeyCursor, WrongCursor};
pub use crate::pagination::{PageInfo, PagedData};
pub use duniter_core::bda_types::MAX_FIRST_UTXOS;
@ -69,15 +72,6 @@ use std::{
str::FromStr,
};
#[derive(Clone, Copy, Debug)]
pub struct WrongCursor;
impl std::fmt::Display for WrongCursor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "wrong cursor")
}
}
impl std::error::Error for WrongCursor {}
#[cfg_attr(feature = "mock", mockall::automock)]
pub trait DbsReader {
fn all_uds_of_pubkey(
@ -181,6 +175,29 @@ pub trait DbsReader {
bn_to_exclude_opt: Option<std::collections::BTreeSet<BlockNumber>>,
amount_target_opt: Option<SourceAmount>,
) -> KvResult<PagedData<uds_of_pubkey::UdsWithSum>>;
fn wallets(
&self,
exclude_single_sig: bool,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<wallets::WalletCursor>,
) -> KvResult<PagedData<Vec<wallets::ScriptWithBalance>>>;
fn wallets_single_sig(
&self,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<PubKeyCursor>,
) -> KvResult<PagedData<Vec<wallets::PublicKeyWithBalance>>>;
fn wallets_single_sig_with_idty_opt(
&self,
bc_db: &BcV2DbRo<FileBackend>,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<PubKeyCursor>,
) -> KvResult<PagedData<Vec<wallets::WalletSingleSigWithIdtyOpt>>>;
fn wallets_with_idty_opt(
&self,
bc_db: &BcV2DbRo<FileBackend>,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<wallets::WalletCursor>,
) -> KvResult<PagedData<Vec<wallets::WalletWithIdtyOpt>>>;
}
#[derive(Clone, Copy, Debug)]
@ -359,6 +376,41 @@ impl DbsReader for DbsReaderImpl {
amount_target_opt,
)
}
fn wallets(
&self,
exclude_single_sig: bool,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<wallets::WalletCursor>,
) -> KvResult<PagedData<Vec<wallets::ScriptWithBalance>>> {
self.wallets_(exclude_single_sig, min_balance_opt, page_info)
}
fn wallets_single_sig(
&self,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<PubKeyCursor>,
) -> KvResult<PagedData<Vec<wallets::PublicKeyWithBalance>>> {
self.wallets_single_sig_(min_balance_opt, page_info)
}
fn wallets_single_sig_with_idty_opt(
&self,
bc_db: &BcV2DbRo<FileBackend>,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<PubKeyCursor>,
) -> KvResult<PagedData<Vec<wallets::WalletSingleSigWithIdtyOpt>>> {
self.wallets_single_sig_with_idty_opt_(bc_db, min_balance_opt, page_info)
}
fn wallets_with_idty_opt(
&self,
bc_db: &BcV2DbRo<FileBackend>,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<wallets::WalletCursor>,
) -> KvResult<PagedData<Vec<wallets::WalletWithIdtyOpt>>> {
self.wallets_with_idty_opt_(bc_db, min_balance_opt, page_info)
}
}
#[cfg(test)]

View file

@ -30,6 +30,19 @@ impl<D: std::fmt::Debug + Default> PagedData<D> {
}
}
}
impl<D: std::fmt::Debug> PagedData<D> {
pub fn map<F, T>(self, f: F) -> PagedData<T>
where
T: std::fmt::Debug,
F: FnOnce(D) -> T,
{
PagedData {
data: f(self.data),
has_previous_page: self.has_previous_page,
has_next_page: self.has_next_page,
}
}
}
#[derive(Debug)]
pub struct PageInfo<T> {

319
dbs-reader/src/wallets.rs Normal file
View file

@ -0,0 +1,319 @@
// Copyright (C) 2020 É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/>.
use crate::*;
use duniter_core::crypto::keys::ed25519::PublicKey;
use duniter_core::crypto::keys::PublicKey as _;
use duniter_core::dbs::{bincode_db, IdtyDbV2, WalletConditionsV2};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WalletCursor(WalletScriptV10);
impl WalletCursor {
pub fn from_ref(script: &WalletScriptV10) -> &Self {
#[allow(trivial_casts)]
unsafe {
&*(script as *const WalletScriptV10 as *const WalletCursor)
}
}
}
impl Cursor for WalletCursor {}
impl Default for WalletCursor {
fn default() -> Self {
WalletCursor(WalletScriptV10::single_sig(PublicKey::default()))
}
}
impl std::fmt::Display for WalletCursor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.to_string())
}
}
impl FromStr for WalletCursor {
type Err = WrongCursor;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(pubkey) = PublicKey::from_base58(s) {
Ok(WalletCursor(WalletScriptV10::single_sig(pubkey)))
} else if let Ok(wallet_script) = duniter_core::documents_parser::wallet_script_from_str(s)
{
Ok(WalletCursor(wallet_script))
} else {
Err(WrongCursor)
}
}
}
impl Ord for WalletCursor {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use bincode::config::Options as _;
let self_bin = bincode_db()
.serialize(&self.0)
.unwrap_or_else(|_| unreachable!());
let other_bin = bincode_db()
.serialize(&other.0)
.unwrap_or_else(|_| unreachable!());
self_bin.cmp(&other_bin)
}
}
impl From<WalletCursor> for WalletConditionsV2 {
fn from(val: WalletCursor) -> Self {
WalletConditionsV2(val.0)
}
}
impl PartialOrd for WalletCursor {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
use bincode::config::Options as _;
let self_bin = bincode_db()
.serialize(&self.0)
.unwrap_or_else(|_| unreachable!());
let other_bin = bincode_db()
.serialize(&other.0)
.unwrap_or_else(|_| unreachable!());
self_bin.partial_cmp(&other_bin)
}
}
#[derive(Clone, Copy, Debug)]
pub struct PublicKeyWithBalance(pub PublicKey, pub SourceAmount);
impl AsRef<PubKeyCursor> for PublicKeyWithBalance {
fn as_ref(&self) -> &PubKeyCursor {
PubKeyCursor::from_ref(&self.0)
}
}
#[derive(Debug)]
pub struct ScriptWithBalance(pub WalletScriptV10, pub SourceAmount);
impl AsRef<WalletCursor> for ScriptWithBalance {
fn as_ref(&self) -> &WalletCursor {
WalletCursor::from_ref(&self.0)
}
}
#[derive(Debug)]
pub struct WalletSingleSigWithIdtyOpt(pub PublicKeyWithBalance, pub Option<IdtyDbV2>);
#[derive(Debug)]
pub struct WalletWithIdtyOpt(pub ScriptWithBalance, pub Option<IdtyDbV2>);
impl DbsReaderImpl {
pub(super) fn wallets_(
&self,
exclude_single_sig: bool,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<WalletCursor>,
) -> KvResult<PagedData<Vec<ScriptWithBalance>>> {
if let Some(min_balance) = min_balance_opt {
if exclude_single_sig {
self.wallets_inner(
|(k, v)| {
if !k.0.is_single_sig() && v.0 >= min_balance {
Some(ScriptWithBalance(k.0, v.0))
} else {
None
}
},
page_info,
)
} else {
self.wallets_inner(
|(k, v)| {
if v.0 >= min_balance {
Some(ScriptWithBalance(k.0, v.0))
} else {
None
}
},
page_info,
)
}
} else if exclude_single_sig {
self.wallets_inner(
|(k, v)| {
if !k.0.is_single_sig() {
Some(ScriptWithBalance(k.0, v.0))
} else {
None
}
},
page_info,
)
} else {
self.wallets_inner(|(k, v)| Some(ScriptWithBalance(k.0, v.0)), page_info)
}
}
pub(super) fn wallets_with_idty_opt_(
&self,
bc_db: &BcV2DbRo<FileBackend>,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<WalletCursor>,
) -> KvResult<PagedData<Vec<WalletWithIdtyOpt>>> {
let paged_data = self.wallets_(false, min_balance_opt, page_info)?;
let mut data = Vec::with_capacity(paged_data.data.len());
for script_with_balance in paged_data.data {
let idty_opt = if let Some(pubkey) = script_with_balance.0.as_single_sig() {
bc_db.identities().get(&PubKeyKeyV2(pubkey))?
} else {
None
};
data.push(WalletWithIdtyOpt(script_with_balance, idty_opt));
}
Ok(PagedData {
data,
has_next_page: paged_data.has_next_page,
has_previous_page: paged_data.has_previous_page,
})
}
pub(super) fn wallets_single_sig_(
&self,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<PubKeyCursor>,
) -> KvResult<PagedData<Vec<PublicKeyWithBalance>>> {
if let Some(min_balance) = min_balance_opt {
self.wallets_inner(
|(k, v)| {
if v.0 >= min_balance {
k.0.as_single_sig().map(|pk| PublicKeyWithBalance(pk, v.0))
} else {
None
}
},
page_info,
)
} else {
self.wallets_inner(
|(k, v)| k.0.as_single_sig().map(|pk| PublicKeyWithBalance(pk, v.0)),
page_info,
)
}
}
pub(super) fn wallets_single_sig_with_idty_opt_(
&self,
bc_db: &BcV2DbRo<FileBackend>,
min_balance_opt: Option<SourceAmount>,
page_info: PageInfo<PubKeyCursor>,
) -> KvResult<PagedData<Vec<WalletSingleSigWithIdtyOpt>>> {
let paged_data = self.wallets_single_sig_(min_balance_opt, page_info)?;
let mut data = Vec::with_capacity(paged_data.data.len());
for pk_with_balance in paged_data.data {
let idty_opt = bc_db.identities().get(&PubKeyKeyV2(pk_with_balance.0))?;
data.push(WalletSingleSigWithIdtyOpt(pk_with_balance, idty_opt));
}
Ok(PagedData {
data,
has_next_page: paged_data.has_next_page,
has_previous_page: paged_data.has_previous_page,
})
}
fn wallets_inner<C, E, F>(
&self,
filter_map: F,
page_info: PageInfo<C>,
) -> KvResult<PagedData<Vec<E>>>
where
C: Cursor + Into<WalletConditionsV2>,
E: AsRef<C> + std::fmt::Debug + Send + Sync,
F: Copy + Fn((WalletConditionsV2, SourceAmountValV2)) -> Option<E>,
{
let first_cursor_opt = if page_info.not_all() {
self.0
.balances()
.iter(.., |it| it.filter_map_ok(filter_map).next_res())?
.map(|element| element.as_ref().to_owned())
} else {
None
};
let last_cursor_opt = if page_info.not_all() {
self.0
.balances()
.iter_rev(.., |it| it.filter_map_ok(filter_map).next_res())?
.map(|element| element.as_ref().to_owned())
} else {
None
};
let cursor_opt = page_info.pos.clone();
let data = if page_info.order {
let first_key = cursor_opt
.unwrap_or_else(|| first_cursor_opt.clone().unwrap_or_default())
.into();
self.0.balances().iter(first_key.., |it| {
if let Some(limit) = page_info.limit_opt {
it.filter_map_ok(filter_map)
.take(limit.get())
.collect::<KvResult<Vec<_>>>()
} else {
it.filter_map_ok(filter_map).collect::<KvResult<Vec<_>>>()
}
})?
} else {
let last_key = cursor_opt
.unwrap_or_else(|| last_cursor_opt.clone().unwrap_or_default())
.into();
self.0.balances().iter_rev(..=last_key, |it| {
if let Some(limit) = page_info.limit_opt {
it.filter_map_ok(filter_map)
.take(limit.get())
.collect::<KvResult<Vec<_>>>()
} else {
it.filter_map_ok(filter_map).collect::<KvResult<Vec<_>>>()
}
})?
};
let page_not_reversed = page_info.order;
Ok(PagedData {
has_next_page: if page_info.order {
has_next_page(
data.iter()
.map(|element| OwnedOrRef::Borrow(element.as_ref())),
last_cursor_opt,
page_info.clone(),
page_not_reversed,
)
} else {
// Server can't efficiently determine hasNextPage in DESC order
false
},
has_previous_page: if page_info.order {
// Server can't efficiently determine hasPreviousPage in ASC order
false
} else {
has_previous_page(
data.iter()
.map(|element| OwnedOrRef::Borrow(element.as_ref())),
first_cursor_opt,
page_info,
page_not_reversed,
)
},
data,
})
}
}

View file

@ -19,6 +19,7 @@ pub mod network;
pub mod tx_gva;
pub mod ud_gva;
pub mod utxos_gva;
pub mod wallet_gva;
use crate::*;
@ -27,11 +28,19 @@ pub(crate) struct AggregateSum {
pub(crate) aggregate: Sum,
}
#[derive(Default, async_graphql::SimpleObject)]
#[derive(Clone, Copy, Debug, Default, async_graphql::SimpleObject)]
pub(crate) struct AmountWithBase {
pub(crate) amount: i32,
pub(crate) base: i32,
}
impl From<SourceAmount> for AmountWithBase {
fn from(sa: SourceAmount) -> Self {
Self {
amount: sa.amount() as i32,
base: sa.base() as i32,
}
}
}
#[derive(async_graphql::SimpleObject)]
pub(crate) struct EdgeTx {

View file

@ -13,7 +13,7 @@
// 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/>.
#[derive(async_graphql::SimpleObject)]
#[derive(Clone, Debug, async_graphql::SimpleObject)]
pub(crate) struct Identity {
pub is_member: bool,
pub username: String,

View file

@ -0,0 +1,41 @@
// Copyright (C) 2020 É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/>.
use crate::*;
#[derive(Clone, Copy, async_graphql::Enum, Eq, PartialEq)]
pub(crate) enum WalletTypeFilter {
/// All wallets
All,
/// Exclude wallets scripts with single SIG condition
OnlyComplex,
/// Only wallets scripts with single SIG condition
OnlySimple,
}
impl Default for WalletTypeFilter {
fn default() -> WalletTypeFilter {
WalletTypeFilter::OnlySimple
}
}
#[derive(Clone, Debug, async_graphql::SimpleObject)]
pub(crate) struct Wallet {
/// Wallet script or public key
pub(crate) script: String,
/// Wallet balance
pub(crate) balance: AmountWithBase,
/// Optional identity attached to this wallet
pub(crate) idty: Option<Identity>,
}

View file

@ -41,6 +41,7 @@ use crate::entities::{
tx_gva::{PendingTxGva, WrittenTxGva},
ud_gva::{CurrentUdGva, RevalUdGva, UdGva},
utxos_gva::UtxosGva,
wallet_gva::{Wallet, WalletTypeFilter},
AggregateSum, AmountWithBase, EdgeTx, RawTxOrChanges, Sum, TxDirection, TxsHistoryMempool,
UtxoGva, UtxoTimedGva,
};

View file

@ -24,6 +24,7 @@ pub mod network;
pub mod txs_history;
pub mod uds;
pub mod utxos_of_script;
pub mod wallets;
use crate::*;
@ -42,6 +43,7 @@ pub struct QueryRoot(
queries::txs_history::TxsHistoryMempoolQuery,
queries::uds::UdsQuery,
queries::utxos_of_script::UtxosQuery,
queries::wallets::WalletsQuery,
);
#[derive(Default, async_graphql::SimpleObject)]

172
gql/src/queries/wallets.rs Normal file
View file

@ -0,0 +1,172 @@
// Copyright (C) 2020 É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/>.
use crate::*;
use async_graphql::connection::*;
use duniter_gva_dbs_reader::{
wallets::{WalletSingleSigWithIdtyOpt, WalletWithIdtyOpt},
PagedData,
};
#[derive(Default)]
pub(crate) struct WalletsQuery;
#[async_graphql::Object]
impl WalletsQuery {
/// Universal dividends issued by a public key
#[allow(clippy::clippy::too_many_arguments)]
async fn wallets(
&self,
ctx: &async_graphql::Context<'_>,
#[graphql(desc = "minimal balance")] min_balance: Option<i64>,
#[graphql(desc = "pagination", default)] pagination: Pagination,
#[graphql(desc = "Wallet type filter", default)] wallet_type_filter: WalletTypeFilter,
) -> async_graphql::Result<Connection<String, Wallet, EmptyFields, EmptyFields>> {
let QueryContext { is_whitelisted } = ctx.data::<QueryContext>()?;
let data = ctx.data::<GvaSchemaData>()?;
let dbs_reader = data.dbs_reader();
let current_base =
if let Some(current_ud) = data.cm_accessor.get_current_meta(|cm| cm.current_ud).await {
current_ud.base()
} else {
0
};
let min_balance_opt = min_balance.map(|amount| SourceAmount::new(amount, current_base));
let PagedData {
data,
has_next_page,
has_previous_page,
}: PagedData<Vec<Wallet>> = match wallet_type_filter {
WalletTypeFilter::OnlyComplex => {
let pagination = Pagination::convert_to_page_info(pagination, *is_whitelisted)?;
data.dbs_pool
.execute(move |_| dbs_reader.wallets(true, min_balance_opt, pagination))
.await??
.map(|data| {
data.into_iter()
.map(|script_with_sa| Wallet {
script: script_with_sa.0.to_string(),
balance: AmountWithBase::from(script_with_sa.1),
idty: None,
})
.collect()
})
}
WalletTypeFilter::OnlySimple => {
let pagination = Pagination::convert_to_page_info(pagination, *is_whitelisted)?;
if ctx
.look_ahead()
.field("edges")
.field("node")
.field("idty")
.exists()
{
data.dbs_pool
.execute(move |shared_dbs| {
dbs_reader.wallets_single_sig_with_idty_opt(
&shared_dbs.bc_db_ro,
min_balance_opt,
pagination,
)
})
.await??
.map(|data| {
data.into_iter()
.map(|WalletSingleSigWithIdtyOpt(pk_with_sa, idty_opt)| Wallet {
script: pk_with_sa.0.to_string(),
balance: AmountWithBase::from(pk_with_sa.1),
idty: idty_opt.map(|idty_db| Identity {
is_member: idty_db.is_member,
username: idty_db.username,
}),
})
.collect()
})
} else {
data.dbs_pool
.execute(move |_| {
dbs_reader.wallets_single_sig(min_balance_opt, pagination)
})
.await??
.map(|data| {
data.into_iter()
.map(|pk_with_sa| Wallet {
script: pk_with_sa.0.to_string(),
balance: AmountWithBase::from(pk_with_sa.1),
idty: None,
})
.collect()
})
}
}
WalletTypeFilter::All => {
let pagination = Pagination::convert_to_page_info(pagination, *is_whitelisted)?;
if ctx
.look_ahead()
.field("edges")
.field("node")
.field("idty")
.exists()
{
data.dbs_pool
.execute(move |shared_dbs| {
dbs_reader.wallets_with_idty_opt(
&shared_dbs.bc_db_ro,
min_balance_opt,
pagination,
)
})
.await??
.map(|data| {
data.into_iter()
.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,
}),
})
.collect()
})
} else {
data.dbs_pool
.execute(move |_| dbs_reader.wallets(false, min_balance_opt, pagination))
.await??
.map(|data| {
data.into_iter()
.map(|script_with_sa| Wallet {
script: script_with_sa.0.to_string(),
balance: AmountWithBase::from(script_with_sa.1),
idty: None,
})
.collect()
})
}
}
};
let mut conn = Connection::new(has_previous_page, has_next_page);
conn.append(
data.into_iter()
.map(|wallet| Edge::new(wallet.script.clone(), wallet)),
);
Ok(conn)
}
}