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
  • duniter-v2s-issue-123-industrialize-releases
  • feat/1/increase-antispam-limits
  • fix/1/add_get_transactions_for_bma
  • fix/1/test-cgeek
  • master
5 results

Target

Select target project
  • nodes/rust/modules/duniter-gva
  • tuxmain/duniter-gva
  • aya/duniter-gva
3 results
Select Git revision
  • certifications
  • edition_cleanup
  • idty_by_username
  • master
4 results
Show changes
Commits on Source (14)
Showing
with 964 additions and 104 deletions
...@@ -694,9 +694,9 @@ checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d" ...@@ -694,9 +694,9 @@ checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
[[package]] [[package]]
name = "dubp" name = "dubp"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66f0006f2695beb0ebeaf9018bf7fc16f92ad2a60bc5522ca9813d257dc501f4" checksum = "429ad631310968b6f0ef8b0621ff685ff03a1f69304174c7d8e7dbed47ec68aa"
dependencies = [ dependencies = [
"dubp-block", "dubp-block",
"dubp-common", "dubp-common",
...@@ -710,9 +710,9 @@ dependencies = [ ...@@ -710,9 +710,9 @@ dependencies = [
[[package]] [[package]]
name = "dubp-block" name = "dubp-block"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4524d9bfb9dff0857d705663138dfc3de0761a322ed7c9a429f2777411f3395c" checksum = "8481be98e093e9f599ef685f8b2880ac273145ce4b12bae45a6fe28b158acb38"
dependencies = [ dependencies = [
"dubp-documents", "dubp-documents",
"dubp-documents-parser", "dubp-documents-parser",
...@@ -725,9 +725,9 @@ dependencies = [ ...@@ -725,9 +725,9 @@ dependencies = [
[[package]] [[package]]
name = "dubp-common" name = "dubp-common"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "270b8470070985a1291e0bde3ec76b61b79a148be135a6d2e372e699b5c50d1c" checksum = "ab01b4870cd960917b638dfd951996dcb19850fdc9868a46d81c7a48c1882f2a"
dependencies = [ dependencies = [
"dup-crypto", "dup-crypto",
"serde", "serde",
...@@ -738,9 +738,9 @@ dependencies = [ ...@@ -738,9 +738,9 @@ dependencies = [
[[package]] [[package]]
name = "dubp-documents" name = "dubp-documents"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd04417e58905daf5e0a7c392e6deb19c6503f6d1aaff92fd12895d701681427" checksum = "38c2667a2c02b657ecce47a2f8d421ddd8f193a77277518aa74585bd42303246"
dependencies = [ dependencies = [
"beef", "beef",
"dubp-wallet", "dubp-wallet",
...@@ -752,9 +752,9 @@ dependencies = [ ...@@ -752,9 +752,9 @@ dependencies = [
[[package]] [[package]]
name = "dubp-documents-parser" name = "dubp-documents-parser"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f4e2aa1ce0bd09b26f710ba725e55901ec0ec47fbe26b43b26071f8cf439962" checksum = "9597df5d36e1e269802341908d8970d58ccde6162b402748f8b8b1bd50c1bec6"
dependencies = [ dependencies = [
"dubp-documents", "dubp-documents",
"json-pest-parser", "json-pest-parser",
...@@ -766,9 +766,9 @@ dependencies = [ ...@@ -766,9 +766,9 @@ dependencies = [
[[package]] [[package]]
name = "dubp-wallet" name = "dubp-wallet"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ee60a2d7fe343782fa4ab3e281e23cdf4313d8ffb029d64bb600dd53a347bb" checksum = "d044529bbf5928e9519b9040516365e6e20f2d65f8c8e565930f5561b8ff0126"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"dubp-common", "dubp-common",
...@@ -781,7 +781,7 @@ dependencies = [ ...@@ -781,7 +781,7 @@ dependencies = [
[[package]] [[package]]
name = "dubp-wot" name = "dubp-wot"
version = "0.11.0" 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 = [ dependencies = [
"log", "log",
"once_cell", "once_cell",
...@@ -793,7 +793,7 @@ dependencies = [ ...@@ -793,7 +793,7 @@ dependencies = [
[[package]] [[package]]
name = "duniter-bc-reader" name = "duniter-bc-reader"
version = "0.1.0" 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 = [ dependencies = [
"anyhow", "anyhow",
"dubp", "dubp",
...@@ -824,9 +824,9 @@ dependencies = [ ...@@ -824,9 +824,9 @@ dependencies = [
[[package]] [[package]]
name = "duniter-bda-types" name = "duniter-bda-types"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "023475a8fefc5dc75f150d3fb0afea984df5be359e1eb061f37a18a1b74be9e7" checksum = "a04e49bc24390430f986d07c43a5cafd431ded3954d31d554efd6c34b77c2788"
dependencies = [ dependencies = [
"arrayvec 0.7.0", "arrayvec 0.7.0",
"bincode", "bincode",
...@@ -840,7 +840,7 @@ dependencies = [ ...@@ -840,7 +840,7 @@ dependencies = [
[[package]] [[package]]
name = "duniter-conf" name = "duniter-conf"
version = "0.1.0" 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 = [ dependencies = [
"anyhow", "anyhow",
"dubp", "dubp",
...@@ -854,7 +854,7 @@ dependencies = [ ...@@ -854,7 +854,7 @@ dependencies = [
[[package]] [[package]]
name = "duniter-core" name = "duniter-core"
version = "1.8.1" 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 = [ dependencies = [
"dubp", "dubp",
"dubp-wot", "dubp-wot",
...@@ -869,7 +869,7 @@ dependencies = [ ...@@ -869,7 +869,7 @@ dependencies = [
[[package]] [[package]]
name = "duniter-dbs" name = "duniter-dbs"
version = "0.1.0" 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 = [ dependencies = [
"arrayvec 0.7.0", "arrayvec 0.7.0",
"bincode", "bincode",
...@@ -892,7 +892,7 @@ dependencies = [ ...@@ -892,7 +892,7 @@ dependencies = [
[[package]] [[package]]
name = "duniter-dbs-write-ops" name = "duniter-dbs-write-ops"
version = "0.1.0" 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 = [ dependencies = [
"chrono", "chrono",
"dubp", "dubp",
...@@ -907,7 +907,7 @@ dependencies = [ ...@@ -907,7 +907,7 @@ dependencies = [
[[package]] [[package]]
name = "duniter-global" name = "duniter-global"
version = "1.8.1" 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 = [ dependencies = [
"async-rwlock", "async-rwlock",
"dubp", "dubp",
...@@ -984,6 +984,7 @@ version = "0.1.0" ...@@ -984,6 +984,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arrayvec 0.7.0", "arrayvec 0.7.0",
"bincode",
"duniter-core", "duniter-core",
"duniter-gva-db", "duniter-gva-db",
"flate2", "flate2",
...@@ -1040,7 +1041,7 @@ dependencies = [ ...@@ -1040,7 +1041,7 @@ dependencies = [
[[package]] [[package]]
name = "duniter-mempools" name = "duniter-mempools"
version = "0.1.0" 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 = [ dependencies = [
"dubp", "dubp",
"duniter-bc-reader", "duniter-bc-reader",
...@@ -1053,7 +1054,7 @@ dependencies = [ ...@@ -1053,7 +1054,7 @@ dependencies = [
[[package]] [[package]]
name = "duniter-module" name = "duniter-module"
version = "0.1.0" 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 = [ dependencies = [
"anyhow", "anyhow",
"async-mutex", "async-mutex",
...@@ -1072,9 +1073,9 @@ dependencies = [ ...@@ -1072,9 +1073,9 @@ dependencies = [
[[package]] [[package]]
name = "duniter-peer" name = "duniter-peer"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1604f510b17e284453cb06f46a9304be9ed65728163ac35f3b22cd5f058952d3" checksum = "952ebba568abb1a8a1a7f83ff960a88b4c1de9cd29c74c6885ecc83ca56cccaf"
dependencies = [ dependencies = [
"beef", "beef",
"dubp-common", "dubp-common",
...@@ -1087,9 +1088,9 @@ dependencies = [ ...@@ -1087,9 +1088,9 @@ dependencies = [
[[package]] [[package]]
name = "dup-crypto" name = "dup-crypto"
version = "0.57.0" version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83e883538079a059b2126a94a13e1d1c62a3a93cd14174eea9d4dd3b4f40cb73" checksum = "f74bc2e8282dd156cd62e54686bc77a22dbaa711facac93b27d6f2c72ff5d07a"
dependencies = [ dependencies = [
"base64", "base64",
"blake3", "blake3",
...@@ -1640,7 +1641,7 @@ dependencies = [ ...@@ -1640,7 +1641,7 @@ dependencies = [
[[package]] [[package]]
name = "kv_typed" name = "kv_typed"
version = "0.1.0" 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 = [ dependencies = [
"byteorder", "byteorder",
"cfg-if 0.1.10", "cfg-if 0.1.10",
......
...@@ -40,6 +40,10 @@ serde_json = "1.0.53" ...@@ -40,6 +40,10 @@ serde_json = "1.0.53"
tokio = { version = "1.2", features = ["macros", "rt-multi-thread", "time"] } tokio = { version = "1.2", features = ["macros", "rt-multi-thread", "time"] }
unwrap = "1.2.1" unwrap = "1.2.1"
[features]
default = ["explorer"]
explorer = ["duniter-gva-db/explorer", "duniter-gva-indexer/explorer"]
[workspace] [workspace]
members = [ members = [
"bda", "bda",
......
...@@ -20,6 +20,7 @@ mod members_count; ...@@ -20,6 +20,7 @@ mod members_count;
mod peers; mod peers;
mod prepare_simple_payment; mod prepare_simple_payment;
mod send_txs; mod send_txs;
mod sync;
mod utxos; mod utxos;
use duniter_core::crypto::keys::KeyPair; use duniter_core::crypto::keys::KeyPair;
...@@ -47,30 +48,26 @@ pub(super) async fn execute_req_type( ...@@ -47,30 +48,26 @@ pub(super) async fn execute_req_type(
BcaReqTypeV0::BalancesOfPubkeys(pubkeys) => { BcaReqTypeV0::BalancesOfPubkeys(pubkeys) => {
balances::exec_req_balances_of_pubkeys(bda_executor, pubkeys).await 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 { BcaReqTypeV0::FirstUtxosOfPubkeys {
amount_target_opt, amount_target_opt,
pubkeys, pubkeys,
} => utxos::exec_req_first_utxos_of_pubkeys(bda_executor, amount_target_opt, pubkeys).await, } => 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) => { BcaReqTypeV0::Identities(pubkeys) => {
let dbs_reader = bda_executor.dbs_reader(); let dbs_reader = bda_executor.dbs_reader();
Ok(BcaRespTypeV0::Identities( Ok(BcaRespTypeV0::Identities(
...@@ -92,10 +89,27 @@ pub(super) async fn execute_req_type( ...@@ -92,10 +89,27 @@ pub(super) async fn execute_req_type(
.await??, .await??,
)) ))
} }
BcaReqTypeV0::CurrentUd => current_ud::exec_req_current_ud(bda_executor).await, BcaReqTypeV0::LastBlockstampOutOfForkWindow => {
BcaReqTypeV0::BalancesOfScripts(scripts) => { last_blockstamp_out_of_fork_window::exec_req_last_blockstamp_out_of_fork_window(
balances::exec_req_balances_of_scripts(bda_executor, scripts).await 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::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 @@ ...@@ -24,9 +24,7 @@
mod exec_req_type; mod exec_req_type;
const MAX_BATCH_SIZE: usize = 10; pub use duniter_core::dbs::kv_typed::prelude::*;
const RESP_MIN_SIZE: usize = 64;
type RespBytes = SmallVec<[u8; RESP_MIN_SIZE]>;
use crate::exec_req_type::ExecReqTypeError; use crate::exec_req_type::ExecReqTypeError;
#[cfg(test)] #[cfg(test)]
...@@ -40,17 +38,20 @@ use duniter_core::bda_types::{ ...@@ -40,17 +38,20 @@ use duniter_core::bda_types::{
BcaResp, BcaRespTypeV0, BcaRespV0, BcaResp, BcaRespTypeV0, BcaRespV0,
}; };
use duniter_core::crypto::keys::{ed25519::Ed25519KeyPair, Signator}; use duniter_core::crypto::keys::{ed25519::Ed25519KeyPair, Signator};
pub use duniter_core::dbs::kv_typed::prelude::*;
use duniter_core::dbs::{FileBackend, SharedDbs}; use duniter_core::dbs::{FileBackend, SharedDbs};
#[cfg(not(test))] #[cfg(not(test))]
use duniter_core::global::AsyncAccessor; use duniter_core::global::AsyncAccessor;
use duniter_gva_dbs_reader::DbsReader; use duniter_gva_dbs_reader::DbsReader;
use futures::{prelude::stream::FuturesUnordered, StreamExt, TryStream, TryStreamExt}; use futures::{prelude::stream::FuturesUnordered, StreamExt, TryStream, TryStreamExt};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::path::PathBuf;
use tokio::task::JoinError; 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)] #[cfg(test)]
use crate::tests::DbsReaderImpl; use crate::tests::DbsReaderImpl;
#[cfg(not(test))] #[cfg(not(test))]
...@@ -58,11 +59,13 @@ use duniter_gva_dbs_reader::DbsReaderImpl; ...@@ -58,11 +59,13 @@ use duniter_gva_dbs_reader::DbsReaderImpl;
static BDA_EXECUTOR: OnceCell<BdaExecutor> = OnceCell::new(); static BDA_EXECUTOR: OnceCell<BdaExecutor> = OnceCell::new();
#[allow(clippy::too_many_arguments)]
pub fn set_bda_executor( pub fn set_bda_executor(
currency: String, currency: String,
cm_accessor: AsyncAccessor, cm_accessor: AsyncAccessor,
dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<SharedDbs<FileBackend>>, dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<SharedDbs<FileBackend>>,
dbs_reader: DbsReaderImpl, dbs_reader: DbsReaderImpl,
profile_path_opt: Option<PathBuf>,
self_keypair: Ed25519KeyPair, self_keypair: Ed25519KeyPair,
software_version: &'static str, software_version: &'static str,
txs_mempool: duniter_core::mempools::TxsMempool, txs_mempool: duniter_core::mempools::TxsMempool,
...@@ -73,6 +76,7 @@ pub fn set_bda_executor( ...@@ -73,6 +76,7 @@ pub fn set_bda_executor(
cm_accessor, cm_accessor,
dbs_pool, dbs_pool,
dbs_reader, dbs_reader,
profile_path_opt,
self_keypair, self_keypair,
software_version, software_version,
txs_mempool, txs_mempool,
...@@ -100,6 +104,7 @@ struct BdaExecutor { ...@@ -100,6 +104,7 @@ struct BdaExecutor {
currency: String, currency: String,
dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<SharedDbs<FileBackend>>, dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<SharedDbs<FileBackend>>,
dbs_reader: DbsReaderImpl, dbs_reader: DbsReaderImpl,
profile_path_opt: Option<PathBuf>,
self_keypair: Ed25519KeyPair, self_keypair: Ed25519KeyPair,
software_version: &'static str, software_version: &'static str,
txs_mempool: duniter_core::mempools::TxsMempool, txs_mempool: duniter_core::mempools::TxsMempool,
...@@ -264,6 +269,7 @@ mod tests { ...@@ -264,6 +269,7 @@ mod tests {
currency: "g1".to_owned(), currency: "g1".to_owned(),
dbs_pool: threadpool.into_async_handler(), dbs_pool: threadpool.into_async_handler(),
dbs_reader: duniter_core::dbs::kv_typed::prelude::Arc::new(mock_dbs_reader), dbs_reader: duniter_core::dbs::kv_typed::prelude::Arc::new(mock_dbs_reader),
profile_path_opt: None,
self_keypair: Ed25519KeyPair::from_seed( self_keypair: Ed25519KeyPair::from_seed(
Seed32::random().expect("fail to gen random seed"), Seed32::random().expect("fail to gen random seed"),
), ),
......
...@@ -30,16 +30,15 @@ pub struct GvaConf { ...@@ -30,16 +30,15 @@ pub struct GvaConf {
#[serde(default = "ip4_default")] #[serde(default = "ip4_default")]
pub ip4: Ipv4Addr, pub ip4: Ipv4Addr,
pub ip6: Option<Ipv6Addr>, pub ip6: Option<Ipv6Addr>,
#[serde(default = "port_default")]
pub port: u16,
#[serde(default = "path_default")] #[serde(default = "path_default")]
pub path: String, pub path: String,
#[serde(default = "subscriptions_path_default")] #[serde(default = "playground_default")]
pub subscriptions_path: String, pub playground: bool,
#[serde(default = "port_default")]
pub port: u16,
pub remote_host: Option<String>, pub remote_host: Option<String>,
pub remote_port: Option<u16>, pub remote_port: Option<u16>,
pub remote_path: Option<String>, pub remote_path: Option<String>,
pub remote_subscriptions_path: Option<String>,
pub remote_tls: Option<bool>, pub remote_tls: Option<bool>,
#[serde(default = "whitelist_default")] #[serde(default = "whitelist_default")]
pub whitelist: Vec<IpAddr>, pub whitelist: Vec<IpAddr>,
...@@ -57,12 +56,12 @@ fn path_default() -> String { ...@@ -57,12 +56,12 @@ fn path_default() -> String {
"gva".to_owned() "gva".to_owned()
} }
const fn port_default() -> u16 { const fn playground_default() -> bool {
30_901 true
} }
fn subscriptions_path_default() -> String { const fn port_default() -> u16 {
"gva-sub".to_owned() 30_901
} }
fn whitelist_default() -> Vec<IpAddr> { fn whitelist_default() -> Vec<IpAddr> {
...@@ -78,13 +77,12 @@ impl Default for GvaConf { ...@@ -78,13 +77,12 @@ impl Default for GvaConf {
enabled: false, enabled: false,
ip4: ip4_default(), ip4: ip4_default(),
ip6: Some(ip6_default()), ip6: Some(ip6_default()),
port: port_default(),
path: path_default(), path: path_default(),
subscriptions_path: subscriptions_path_default(), port: port_default(),
playground: playground_default(),
remote_host: None, remote_host: None,
remote_port: None, remote_port: None,
remote_path: None, remote_path: None,
remote_subscriptions_path: None,
remote_tls: None, remote_tls: None,
whitelist: whitelist_default(), whitelist: whitelist_default(),
} }
...@@ -100,11 +98,6 @@ impl GvaConf { ...@@ -100,11 +98,6 @@ impl GvaConf {
.clone() .clone()
.unwrap_or_else(|| self.path.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)] #[derive(StructOpt)]
...@@ -164,14 +157,9 @@ impl GvaCommand { ...@@ -164,14 +157,9 @@ impl GvaCommand {
.msg(format!("Path ? [{}]", conf.path)) .msg(format!("Path ? [{}]", conf.path))
.default(conf.path) .default(conf.path)
.get(); .get();
// subscriptionsPath // playground
conf.subscriptions_path = input() let res = input().msg("Enable playground ? [Y/n]").default('Y').get();
.msg(format!( conf.playground = res != 'n';
"Subscriptions path ? [{}]",
conf.subscriptions_path
))
.default(conf.subscriptions_path)
.get();
// remoteHost // remoteHost
if let Some(ref remote_host) = conf.remote_host { if let Some(ref remote_host) = conf.remote_host {
let new_remote_host = input() let new_remote_host = input()
...@@ -213,17 +201,6 @@ impl GvaCommand { ...@@ -213,17 +201,6 @@ impl GvaCommand {
} else { } else {
conf.remote_path = None; 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 // whitelist
let mut whitelist: HashSet<_> = conf.whitelist.iter().copied().collect(); let mut whitelist: HashSet<_> = conf.whitelist.iter().copied().collect();
let res = input().msg("Update whitelist? [y/N]").default('N').get(); let res = input().msg("Update whitelist? [y/N]").default('N').get();
......
...@@ -49,6 +49,8 @@ pub(crate) use duniter_core::wot::WotId; ...@@ -49,6 +49,8 @@ pub(crate) use duniter_core::wot::WotId;
pub(crate) use serde::{Deserialize, Serialize}; pub(crate) use serde::{Deserialize, Serialize};
pub(crate) use std::collections::BTreeSet; pub(crate) use std::collections::BTreeSet;
pub const BLOCKS_CHUNK_SIZE: u32 = 4_096;
db_schema!( db_schema!(
GvaV1, GvaV1,
[ [
......
...@@ -17,6 +17,7 @@ mock = ["mockall"] ...@@ -17,6 +17,7 @@ mock = ["mockall"]
[dependencies] [dependencies]
anyhow = "1.0.34" anyhow = "1.0.34"
arrayvec = { version = "0.7", features = ["serde"] } arrayvec = { version = "0.7", features = ["serde"] }
bincode = "1.3"
duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core" } duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core" }
duniter-gva-db = { path = "../db" } duniter-gva-db = { path = "../db" }
flate2 = { version = "1.0", features = ["zlib-ng-compat"], default-features = false } flate2 = { version = "1.0", features = ["zlib-ng-compat"], default-features = false }
......
...@@ -19,6 +19,26 @@ use flate2::read::ZlibDecoder; ...@@ -19,6 +19,26 @@ use flate2::read::ZlibDecoder;
pub const CHUNK_FILE_PREFIX: &str = "_"; pub const CHUNK_FILE_PREFIX: &str = "_";
pub const CHUNK_FILE_EXT: &str = ".bin.gz"; 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 /// Read and decompress chunk file
pub fn read_compressed_chunk( pub fn read_compressed_chunk(
chunk_index: u32, 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 @@ ...@@ -25,6 +25,7 @@
pub mod block; pub mod block;
pub mod blocks_chunks; pub mod blocks_chunks;
pub mod current_frame; pub mod current_frame;
pub mod cursors;
pub mod find_inputs; pub mod find_inputs;
pub mod idty; pub mod idty;
pub mod network; pub mod network;
...@@ -32,7 +33,9 @@ pub mod pagination; ...@@ -32,7 +33,9 @@ pub mod pagination;
pub mod txs_history; pub mod txs_history;
pub mod uds_of_pubkey; pub mod uds_of_pubkey;
pub mod utxos; pub mod utxos;
pub mod wallets;
pub use crate::cursors::{Cursor, PubKeyCursor, WrongCursor};
pub use crate::pagination::{PageInfo, PagedData}; pub use crate::pagination::{PageInfo, PagedData};
pub use duniter_core::bda_types::MAX_FIRST_UTXOS; pub use duniter_core::bda_types::MAX_FIRST_UTXOS;
...@@ -69,15 +72,6 @@ use std::{ ...@@ -69,15 +72,6 @@ use std::{
str::FromStr, 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)] #[cfg_attr(feature = "mock", mockall::automock)]
pub trait DbsReader { pub trait DbsReader {
fn all_uds_of_pubkey( fn all_uds_of_pubkey(
...@@ -92,6 +86,7 @@ pub trait DbsReader { ...@@ -92,6 +86,7 @@ pub trait DbsReader {
bc_db: &BcV2DbRo<FileBackend>, bc_db: &BcV2DbRo<FileBackend>,
page_info: PageInfo<block::BlockCursor>, page_info: PageInfo<block::BlockCursor>,
) -> KvResult<PagedData<Vec<(block::BlockCursor, BlockMetaV2)>>>; ) -> 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>( fn endpoints<Db: 'static + NetworkV1DbReadable>(
&self, &self,
network_db: &Db, network_db: &Db,
...@@ -181,6 +176,29 @@ pub trait DbsReader { ...@@ -181,6 +176,29 @@ pub trait DbsReader {
bn_to_exclude_opt: Option<std::collections::BTreeSet<BlockNumber>>, bn_to_exclude_opt: Option<std::collections::BTreeSet<BlockNumber>>,
amount_target_opt: Option<SourceAmount>, amount_target_opt: Option<SourceAmount>,
) -> KvResult<PagedData<uds_of_pubkey::UdsWithSum>>; ) -> 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)] #[derive(Clone, Copy, Debug)]
...@@ -212,6 +230,10 @@ impl DbsReader for DbsReaderImpl { ...@@ -212,6 +230,10 @@ impl DbsReader for DbsReaderImpl {
self.blocks_(bc_db, page_info) 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>( fn endpoints<Db: 'static + NetworkV1DbReadable>(
&self, &self,
network_db: &Db, network_db: &Db,
...@@ -359,6 +381,41 @@ impl DbsReader for DbsReaderImpl { ...@@ -359,6 +381,41 @@ impl DbsReader for DbsReaderImpl {
amount_target_opt, 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)] #[cfg(test)]
......
...@@ -30,6 +30,19 @@ impl<D: std::fmt::Debug + Default> PagedData<D> { ...@@ -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)] #[derive(Debug)]
pub struct PageInfo<T> { 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; ...@@ -19,6 +19,7 @@ pub mod network;
pub mod tx_gva; pub mod tx_gva;
pub mod ud_gva; pub mod ud_gva;
pub mod utxos_gva; pub mod utxos_gva;
pub mod wallet_gva;
use crate::*; use crate::*;
...@@ -27,11 +28,49 @@ pub(crate) struct AggregateSum { ...@@ -27,11 +28,49 @@ pub(crate) struct AggregateSum {
pub(crate) aggregate: Sum, pub(crate) aggregate: Sum,
} }
#[derive(Default, async_graphql::SimpleObject)] #[derive(Clone, Copy, Debug, Default, async_graphql::SimpleObject)]
pub(crate) struct AmountWithBase { pub(crate) struct AmountWithBase {
pub(crate) amount: i32, pub(crate) amount: i32,
pub(crate) base: 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)] #[derive(async_graphql::SimpleObject)]
pub(crate) struct EdgeTx { pub(crate) struct EdgeTx {
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
// You should have received a copy of the GNU Affero General Public License // 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/>. // 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(crate) struct Identity {
pub is_member: bool, pub is_member: bool,
pub username: String, 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::{ ...@@ -41,6 +41,7 @@ use crate::entities::{
tx_gva::{PendingTxGva, WrittenTxGva}, tx_gva::{PendingTxGva, WrittenTxGva},
ud_gva::{CurrentUdGva, RevalUdGva, UdGva}, ud_gva::{CurrentUdGva, RevalUdGva, UdGva},
utxos_gva::UtxosGva, utxos_gva::UtxosGva,
wallet_gva::{Wallet, WalletTypeFilter},
AggregateSum, AmountWithBase, EdgeTx, RawTxOrChanges, Sum, TxDirection, TxsHistoryMempool, AggregateSum, AmountWithBase, EdgeTx, RawTxOrChanges, Sum, TxDirection, TxsHistoryMempool,
UtxoGva, UtxoTimedGva, UtxoGva, UtxoTimedGva,
}; };
......
...@@ -58,7 +58,7 @@ impl Pagination { ...@@ -58,7 +58,7 @@ impl Pagination {
self, self,
is_whitelisted: bool, is_whitelisted: bool,
) -> anyhow::Result<duniter_gva_dbs_reader::PageInfo<T>> { ) -> 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) NonZeroUsize::new(self.page_size as usize)
} else { } else {
......
...@@ -24,6 +24,7 @@ pub mod network; ...@@ -24,6 +24,7 @@ pub mod network;
pub mod txs_history; pub mod txs_history;
pub mod uds; pub mod uds;
pub mod utxos_of_script; pub mod utxos_of_script;
pub mod wallets;
use crate::*; use crate::*;
...@@ -42,6 +43,7 @@ pub struct QueryRoot( ...@@ -42,6 +43,7 @@ pub struct QueryRoot(
queries::txs_history::TxsHistoryMempoolQuery, queries::txs_history::TxsHistoryMempoolQuery,
queries::uds::UdsQuery, queries::uds::UdsQuery,
queries::utxos_of_script::UtxosQuery, queries::utxos_of_script::UtxosQuery,
queries::wallets::WalletsQuery,
); );
#[derive(Default, async_graphql::SimpleObject)] #[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)
}
}