Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • nodes/rust/modules/duniter-gva
  • tuxmain/duniter-gva
  • aya/duniter-gva
3 results
Select Git revision
Show changes
Commits on Source (14)
Showing
with 964 additions and 104 deletions
......@@ -694,9 +694,9 @@ checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
[[package]]
name = "dubp"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66f0006f2695beb0ebeaf9018bf7fc16f92ad2a60bc5522ca9813d257dc501f4"
checksum = "429ad631310968b6f0ef8b0621ff685ff03a1f69304174c7d8e7dbed47ec68aa"
dependencies = [
"dubp-block",
"dubp-common",
......@@ -710,9 +710,9 @@ dependencies = [
[[package]]
name = "dubp-block"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4524d9bfb9dff0857d705663138dfc3de0761a322ed7c9a429f2777411f3395c"
checksum = "8481be98e093e9f599ef685f8b2880ac273145ce4b12bae45a6fe28b158acb38"
dependencies = [
"dubp-documents",
"dubp-documents-parser",
......@@ -725,9 +725,9 @@ dependencies = [
[[package]]
name = "dubp-common"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "270b8470070985a1291e0bde3ec76b61b79a148be135a6d2e372e699b5c50d1c"
checksum = "ab01b4870cd960917b638dfd951996dcb19850fdc9868a46d81c7a48c1882f2a"
dependencies = [
"dup-crypto",
"serde",
......@@ -738,9 +738,9 @@ dependencies = [
[[package]]
name = "dubp-documents"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd04417e58905daf5e0a7c392e6deb19c6503f6d1aaff92fd12895d701681427"
checksum = "38c2667a2c02b657ecce47a2f8d421ddd8f193a77277518aa74585bd42303246"
dependencies = [
"beef",
"dubp-wallet",
......@@ -752,9 +752,9 @@ dependencies = [
[[package]]
name = "dubp-documents-parser"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f4e2aa1ce0bd09b26f710ba725e55901ec0ec47fbe26b43b26071f8cf439962"
checksum = "9597df5d36e1e269802341908d8970d58ccde6162b402748f8b8b1bd50c1bec6"
dependencies = [
"dubp-documents",
"json-pest-parser",
......@@ -766,9 +766,9 @@ dependencies = [
[[package]]
name = "dubp-wallet"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ee60a2d7fe343782fa4ab3e281e23cdf4313d8ffb029d64bb600dd53a347bb"
checksum = "d044529bbf5928e9519b9040516365e6e20f2d65f8c8e565930f5561b8ff0126"
dependencies = [
"byteorder",
"dubp-common",
......@@ -781,7 +781,7 @@ dependencies = [
[[package]]
name = "dubp-wot"
version = "0.11.0"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"log",
"once_cell",
......@@ -793,7 +793,7 @@ dependencies = [
[[package]]
name = "duniter-bc-reader"
version = "0.1.0"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"anyhow",
"dubp",
......@@ -824,9 +824,9 @@ dependencies = [
[[package]]
name = "duniter-bda-types"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "023475a8fefc5dc75f150d3fb0afea984df5be359e1eb061f37a18a1b74be9e7"
checksum = "a04e49bc24390430f986d07c43a5cafd431ded3954d31d554efd6c34b77c2788"
dependencies = [
"arrayvec 0.7.0",
"bincode",
......@@ -840,7 +840,7 @@ dependencies = [
[[package]]
name = "duniter-conf"
version = "0.1.0"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"anyhow",
"dubp",
......@@ -854,7 +854,7 @@ dependencies = [
[[package]]
name = "duniter-core"
version = "1.8.1"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"dubp",
"dubp-wot",
......@@ -869,7 +869,7 @@ dependencies = [
[[package]]
name = "duniter-dbs"
version = "0.1.0"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"arrayvec 0.7.0",
"bincode",
......@@ -892,7 +892,7 @@ dependencies = [
[[package]]
name = "duniter-dbs-write-ops"
version = "0.1.0"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"chrono",
"dubp",
......@@ -907,7 +907,7 @@ dependencies = [
[[package]]
name = "duniter-global"
version = "1.8.1"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"async-rwlock",
"dubp",
......@@ -984,6 +984,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"arrayvec 0.7.0",
"bincode",
"duniter-core",
"duniter-gva-db",
"flate2",
......@@ -1040,7 +1041,7 @@ dependencies = [
[[package]]
name = "duniter-mempools"
version = "0.1.0"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"dubp",
"duniter-bc-reader",
......@@ -1053,7 +1054,7 @@ dependencies = [
[[package]]
name = "duniter-module"
version = "0.1.0"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"anyhow",
"async-mutex",
......@@ -1072,9 +1073,9 @@ dependencies = [
[[package]]
name = "duniter-peer"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1604f510b17e284453cb06f46a9304be9ed65728163ac35f3b22cd5f058952d3"
checksum = "952ebba568abb1a8a1a7f83ff960a88b4c1de9cd29c74c6885ecc83ca56cccaf"
dependencies = [
"beef",
"dubp-common",
......@@ -1087,9 +1088,9 @@ dependencies = [
[[package]]
name = "dup-crypto"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83e883538079a059b2126a94a13e1d1c62a3a93cd14174eea9d4dd3b4f40cb73"
checksum = "f74bc2e8282dd156cd62e54686bc77a22dbaa711facac93b27d6f2c72ff5d07a"
dependencies = [
"base64",
"blake3",
......@@ -1640,7 +1641,7 @@ dependencies = [
[[package]]
name = "kv_typed"
version = "0.1.0"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#56dd9795ec8e2ee053f4c8ce9e020bd1e335300d"
source = "git+https://git.duniter.org/nodes/rust/duniter-core#eebd168525aa1d1424ad12ad71da768471207d83"
dependencies = [
"byteorder",
"cfg-if 0.1.10",
......
......@@ -40,6 +40,10 @@ serde_json = "1.0.53"
tokio = { version = "1.2", features = ["macros", "rt-multi-thread", "time"] }
unwrap = "1.2.1"
[features]
default = ["explorer"]
explorer = ["duniter-gva-db/explorer", "duniter-gva-indexer/explorer"]
[workspace]
members = [
"bda",
......
......@@ -20,6 +20,7 @@ mod members_count;
mod peers;
mod prepare_simple_payment;
mod send_txs;
mod sync;
mod utxos;
use duniter_core::crypto::keys::KeyPair;
......@@ -47,30 +48,26 @@ pub(super) async fn execute_req_type(
BcaReqTypeV0::BalancesOfPubkeys(pubkeys) => {
balances::exec_req_balances_of_pubkeys(bda_executor, pubkeys).await
}
BcaReqTypeV0::BalancesOfScripts(scripts) => {
balances::exec_req_balances_of_scripts(bda_executor, scripts).await
}
BcaReqTypeV0::CompressedBlockChunk { chunk_id } => {
sync::exec_req_compressed_block_chunk(bda_executor, chunk_id).await
}
BcaReqTypeV0::CurrentBlockstamp => {
if let Some(current_meta) = bda_executor.cm_accessor.get_current_meta(|cm| *cm).await {
Ok(BcaRespTypeV0::CurrentBlockstamp(
current_meta.current_block_meta.blockstamp(),
))
} else {
Err("no blockchain".into())
}
}
BcaReqTypeV0::CurrentUd => current_ud::exec_req_current_ud(bda_executor).await,
BcaReqTypeV0::FirstUtxosOfPubkeys {
amount_target_opt,
pubkeys,
} => utxos::exec_req_first_utxos_of_pubkeys(bda_executor, amount_target_opt, pubkeys).await,
BcaReqTypeV0::LastBlockstampOutOfForkWindow => {
last_blockstamp_out_of_fork_window::exec_req_last_blockstamp_out_of_fork_window(
bda_executor,
)
.await
}
BcaReqTypeV0::MembersCount => members_count::exec_req_members_count(bda_executor).await,
BcaReqTypeV0::PrepareSimplePayment(params) => {
prepare_simple_payment::exec_req_prepare_simple_payment(bda_executor, params).await
}
BcaReqTypeV0::ProofServerPubkey { challenge } => Ok(BcaRespTypeV0::ProofServerPubkey {
challenge,
server_pubkey: bda_executor.self_keypair.public_key(),
sig: bda_executor
.self_keypair
.generate_signator()
.sign(&challenge),
}),
BcaReqTypeV0::Ping => Ok(BcaRespTypeV0::Pong),
BcaReqTypeV0::SendTxs(txs) => send_txs::send_txs(bda_executor, txs).await,
BcaReqTypeV0::Identities(pubkeys) => {
let dbs_reader = bda_executor.dbs_reader();
Ok(BcaRespTypeV0::Identities(
......@@ -92,10 +89,27 @@ pub(super) async fn execute_req_type(
.await??,
))
}
BcaReqTypeV0::CurrentUd => current_ud::exec_req_current_ud(bda_executor).await,
BcaReqTypeV0::BalancesOfScripts(scripts) => {
balances::exec_req_balances_of_scripts(bda_executor, scripts).await
BcaReqTypeV0::LastBlockstampOutOfForkWindow => {
last_blockstamp_out_of_fork_window::exec_req_last_blockstamp_out_of_fork_window(
bda_executor,
)
.await
}
BcaReqTypeV0::MembersCount => members_count::exec_req_members_count(bda_executor).await,
BcaReqTypeV0::PrepareSimplePayment(params) => {
prepare_simple_payment::exec_req_prepare_simple_payment(bda_executor, params).await
}
BcaReqTypeV0::ProofServerPubkey { challenge } => Ok(BcaRespTypeV0::ProofServerPubkey {
challenge,
server_pubkey: bda_executor.self_keypair.public_key(),
sig: bda_executor
.self_keypair
.generate_signator()
.sign(&challenge),
}),
BcaReqTypeV0::Ping => Ok(BcaRespTypeV0::Pong),
BcaReqTypeV0::PeersV10 { n } => peers::exec_req_peers_v1(bda_executor, n).await,
BcaReqTypeV0::SendTxs(txs) => send_txs::send_txs(bda_executor, txs).await,
BcaReqTypeV0::Sync { from, to } => sync::exec_req_sync(bda_executor, from, to).await,
}
}
// 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::bda_types::peer::Peer;
use duniter_gva_db::BLOCKS_CHUNK_SIZE;
pub(super) async fn exec_req_compressed_block_chunk(
bda_executor: &BdaExecutor,
chunk_id: usize,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
if let Some(ref profile_path) = bda_executor.profile_path_opt {
let chunks_folder_path = profile_path.join("data/gva_v1_blocks_chunks");
if let Some(chunk_data) = duniter_gva_dbs_reader::blocks_chunks::read_compressed_chunk(
chunk_id as u32,
&chunks_folder_path,
false,
)? {
Ok(BcaRespTypeV0::CompressedBlockChunk {
chunk_id,
compressed_data: chunk_data,
})
} else {
Err("not found".into())
}
} else {
Err("Cannot get compressed block chunk in memony mode".into())
}
}
pub(super) async fn exec_req_sync(
bda_executor: &BdaExecutor,
from: u32,
to_opt: Option<u32>,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
let (target_blockstamp, last_chunk_id_opt) = if let Some(to) = to_opt {
if to < from {
return Err("to < from".into());
}
let dbs_reader = bda_executor.dbs_reader();
if let Some(block_meta) = bda_executor
.dbs_pool
.execute(move |shared_dbs| dbs_reader.block(&shared_dbs.bc_db_ro, U32BE(to)))
.await??
{
(block_meta.blockstamp(), Some(to / BLOCKS_CHUNK_SIZE))
} else {
return Err("Not enough blocks, try another server".into());
}
} else if let Some(current_meta) = bda_executor.cm_accessor.get_current_meta(|cm| *cm).await {
(current_meta.current_block_meta.blockstamp(), None)
} else {
return Err("no blockchain".into());
};
let first_chunk_id = (from + 1) / BLOCKS_CHUNK_SIZE;
let dbs_reader = bda_executor.dbs_reader();
let (peers, blocks_chunks_hashs) = bda_executor
.dbs_pool
.execute(move |shared_dbs| {
Ok::<_, KvError>((
dbs_reader
.get_some_peers(&shared_dbs.dunp_db, 0)?
.into_iter()
.map(|peer_db| Peer {
peer: peer_db.peer,
is_member: peer_db.member,
is_up: peer_db.status,
})
.collect(),
dbs_reader.blocks_chunks_hashs(first_chunk_id, last_chunk_id_opt)?,
))
})
.await??;
Ok(BcaRespTypeV0::Sync {
blocks_chunks_hashs,
peers,
target_blockstamp,
})
}
......@@ -24,9 +24,7 @@
mod exec_req_type;
const MAX_BATCH_SIZE: usize = 10;
const RESP_MIN_SIZE: usize = 64;
type RespBytes = SmallVec<[u8; RESP_MIN_SIZE]>;
pub use duniter_core::dbs::kv_typed::prelude::*;
use crate::exec_req_type::ExecReqTypeError;
#[cfg(test)]
......@@ -40,17 +38,20 @@ use duniter_core::bda_types::{
BcaResp, BcaRespTypeV0, BcaRespV0,
};
use duniter_core::crypto::keys::{ed25519::Ed25519KeyPair, Signator};
pub use duniter_core::dbs::kv_typed::prelude::*;
use duniter_core::dbs::{FileBackend, SharedDbs};
#[cfg(not(test))]
use duniter_core::global::AsyncAccessor;
use duniter_gva_dbs_reader::DbsReader;
use futures::{prelude::stream::FuturesUnordered, StreamExt, TryStream, TryStreamExt};
use once_cell::sync::OnceCell;
use smallvec::SmallVec;
use std::path::PathBuf;
use tokio::task::JoinError;
const MAX_BATCH_SIZE: usize = 10;
const RESP_MIN_SIZE: usize = 64;
type RespBytes = SmallVec<[u8; RESP_MIN_SIZE]>;
#[cfg(test)]
use crate::tests::DbsReaderImpl;
#[cfg(not(test))]
......@@ -58,11 +59,13 @@ use duniter_gva_dbs_reader::DbsReaderImpl;
static BDA_EXECUTOR: OnceCell<BdaExecutor> = OnceCell::new();
#[allow(clippy::too_many_arguments)]
pub fn set_bda_executor(
currency: String,
cm_accessor: AsyncAccessor,
dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<SharedDbs<FileBackend>>,
dbs_reader: DbsReaderImpl,
profile_path_opt: Option<PathBuf>,
self_keypair: Ed25519KeyPair,
software_version: &'static str,
txs_mempool: duniter_core::mempools::TxsMempool,
......@@ -73,6 +76,7 @@ pub fn set_bda_executor(
cm_accessor,
dbs_pool,
dbs_reader,
profile_path_opt,
self_keypair,
software_version,
txs_mempool,
......@@ -100,6 +104,7 @@ struct BdaExecutor {
currency: String,
dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<SharedDbs<FileBackend>>,
dbs_reader: DbsReaderImpl,
profile_path_opt: Option<PathBuf>,
self_keypair: Ed25519KeyPair,
software_version: &'static str,
txs_mempool: duniter_core::mempools::TxsMempool,
......@@ -264,6 +269,7 @@ mod tests {
currency: "g1".to_owned(),
dbs_pool: threadpool.into_async_handler(),
dbs_reader: duniter_core::dbs::kv_typed::prelude::Arc::new(mock_dbs_reader),
profile_path_opt: None,
self_keypair: Ed25519KeyPair::from_seed(
Seed32::random().expect("fail to gen random seed"),
),
......
......@@ -30,16 +30,15 @@ pub struct GvaConf {
#[serde(default = "ip4_default")]
pub ip4: Ipv4Addr,
pub ip6: Option<Ipv6Addr>,
#[serde(default = "port_default")]
pub port: u16,
#[serde(default = "path_default")]
pub path: String,
#[serde(default = "subscriptions_path_default")]
pub subscriptions_path: String,
#[serde(default = "playground_default")]
pub playground: bool,
#[serde(default = "port_default")]
pub port: u16,
pub remote_host: Option<String>,
pub remote_port: Option<u16>,
pub remote_path: Option<String>,
pub remote_subscriptions_path: Option<String>,
pub remote_tls: Option<bool>,
#[serde(default = "whitelist_default")]
pub whitelist: Vec<IpAddr>,
......@@ -57,12 +56,12 @@ fn path_default() -> String {
"gva".to_owned()
}
const fn port_default() -> u16 {
30_901
const fn playground_default() -> bool {
true
}
fn subscriptions_path_default() -> String {
"gva-sub".to_owned()
const fn port_default() -> u16 {
30_901
}
fn whitelist_default() -> Vec<IpAddr> {
......@@ -78,13 +77,12 @@ impl Default for GvaConf {
enabled: false,
ip4: ip4_default(),
ip6: Some(ip6_default()),
port: port_default(),
path: path_default(),
subscriptions_path: subscriptions_path_default(),
port: port_default(),
playground: playground_default(),
remote_host: None,
remote_port: None,
remote_path: None,
remote_subscriptions_path: None,
remote_tls: None,
whitelist: whitelist_default(),
}
......@@ -100,11 +98,6 @@ impl GvaConf {
.clone()
.unwrap_or_else(|| self.path.clone())
}
pub fn get_remote_subscriptions_path(&self) -> String {
self.remote_subscriptions_path
.clone()
.unwrap_or_else(|| self.subscriptions_path.clone())
}
}
#[derive(StructOpt)]
......@@ -164,14 +157,9 @@ impl GvaCommand {
.msg(format!("Path ? [{}]", conf.path))
.default(conf.path)
.get();
// subscriptionsPath
conf.subscriptions_path = input()
.msg(format!(
"Subscriptions path ? [{}]",
conf.subscriptions_path
))
.default(conf.subscriptions_path)
.get();
// playground
let res = input().msg("Enable playground ? [Y/n]").default('Y').get();
conf.playground = res != 'n';
// remoteHost
if let Some(ref remote_host) = conf.remote_host {
let new_remote_host = input()
......@@ -213,17 +201,6 @@ impl GvaCommand {
} else {
conf.remote_path = None;
}
// remoteSubscriptionsPath
let res = input()
.msg("Define a remote subscriptions path? [y/N]")
.default('N')
.get();
if res == 'y' || res == 'Y' {
conf.remote_subscriptions_path =
Some(input().msg("Enter remote subscriptions path:").get());
} else {
conf.remote_subscriptions_path = None;
}
// whitelist
let mut whitelist: HashSet<_> = conf.whitelist.iter().copied().collect();
let res = input().msg("Update whitelist? [y/N]").default('N').get();
......
......@@ -49,6 +49,8 @@ pub(crate) use duniter_core::wot::WotId;
pub(crate) use serde::{Deserialize, Serialize};
pub(crate) use std::collections::BTreeSet;
pub const BLOCKS_CHUNK_SIZE: u32 = 4_096;
db_schema!(
GvaV1,
[
......
......@@ -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 }
......
......@@ -19,6 +19,26 @@ use flate2::read::ZlibDecoder;
pub const CHUNK_FILE_PREFIX: &str = "_";
pub const CHUNK_FILE_EXT: &str = ".bin.gz";
impl DbsReaderImpl {
pub(super) fn blocks_chunks_hashs_(
&self,
from: u32,
to_opt: Option<u32>,
) -> KvResult<Vec<Hash>> {
if let Some(to) = to_opt {
self.0
.blocks_chunk_hash()
.iter(U32BE(from)..=U32BE(to), |it| {
it.values().map_ok(|hash_db| hash_db.0).collect()
})
} else {
self.0.blocks_chunk_hash().iter(U32BE(from).., |it| {
it.values().map_ok(|hash_db| hash_db.0).collect()
})
}
}
}
/// Read and decompress chunk file
pub fn read_compressed_chunk(
chunk_index: u32,
......
// 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())
}
}
......@@ -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(
......@@ -92,6 +86,7 @@ pub trait DbsReader {
bc_db: &BcV2DbRo<FileBackend>,
page_info: PageInfo<block::BlockCursor>,
) -> KvResult<PagedData<Vec<(block::BlockCursor, BlockMetaV2)>>>;
fn blocks_chunks_hashs(&self, from: u32, to_opt: Option<u32>) -> KvResult<Vec<Hash>>;
fn endpoints<Db: 'static + NetworkV1DbReadable>(
&self,
network_db: &Db,
......@@ -181,6 +176,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)]
......@@ -212,6 +230,10 @@ impl DbsReader for DbsReaderImpl {
self.blocks_(bc_db, page_info)
}
fn blocks_chunks_hashs(&self, from: u32, to_opt: Option<u32>) -> KvResult<Vec<Hash>> {
self.blocks_chunks_hashs_(from, to_opt)
}
fn endpoints<Db: 'static + NetworkV1DbReadable>(
&self,
network_db: &Db,
......@@ -359,6 +381,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)]
......
......@@ -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> {
......
// 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,
})
}
}
......@@ -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,49 @@ 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 AmountWithBase {
fn increment_base(self) -> Self {
Self {
amount: self.amount / 10,
base: self.base + 1,
}
}
}
impl std::ops::Add for AmountWithBase {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
#[allow(clippy::comparison_chain)]
if self.base == rhs.base {
Self {
amount: self.amount + rhs.amount,
base: self.base,
}
} else if self.base > rhs.base {
self.add(rhs.increment_base())
} else {
self.increment_base().add(rhs)
}
}
}
impl From<SourceAmount> for AmountWithBase {
fn from(sa: SourceAmount) -> Self {
Self {
amount: sa.amount() as i32,
base: sa.base() as i32,
}
}
}
impl std::iter::Sum for AmountWithBase {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(AmountWithBase::default(), std::ops::Add::add)
}
}
#[derive(async_graphql::SimpleObject)]
pub(crate) struct EdgeTx {
......
......@@ -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,
......
// 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>,
}
......@@ -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,
};
......
......@@ -58,7 +58,7 @@ impl Pagination {
self,
is_whitelisted: bool,
) -> anyhow::Result<duniter_gva_dbs_reader::PageInfo<T>> {
let page_size = if is_whitelisted || (self.page_size > 0 && self.page_size < MAX_PAGE_SIZE)
let page_size = if is_whitelisted || (self.page_size > 0 && self.page_size <= MAX_PAGE_SIZE)
{
NonZeroUsize::new(self.page_size as usize)
} else {
......
......@@ -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)]
......
// 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, AggregateSum, 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 sum = if ctx.look_ahead().field("aggregate").field("sum").exists() {
data.iter().map(|wallet| wallet.balance).sum()
} else {
AmountWithBase::default()
};
let mut conn = Connection::with_additional_fields(
has_previous_page,
has_next_page,
AggregateSum {
aggregate: Sum { sum },
},
);
conn.append(
data.into_iter()
.map(|wallet| Edge::new(wallet.script.clone(), wallet)),
);
Ok(conn)
}
}