Skip to content
Snippets Groups Projects
Commit 44c04b57 authored by Éloïs's avatar Éloïs
Browse files

Merge branch 'gva/first-utxos-scripts' into dev

parents ba17dba6 c3d0fb4d
No related branches found
No related tags found
1 merge request!1362[feat] gva: add query firstUtxosOfScripts
......@@ -1205,6 +1205,7 @@ name = "duniter-gva-dbs-reader"
version = "0.1.0"
dependencies = [
"anyhow",
"arrayvec",
"dubp",
"duniter-dbs",
"duniter-gva-db",
......
......@@ -13,6 +13,7 @@ path = "src/lib.rs"
[dependencies]
anyhow = "1.0.34"
arrayvec = "0.5.1"
duniter-dbs = { path = "../../../duniter-dbs" }
duniter-gva-db = { path = "../db" }
dubp = { version = "0.47.0" }
......
......@@ -34,6 +34,7 @@ pub mod utxos;
pub use crate::pagination::{PageInfo, PagedData};
use crate::pagination::{has_next_page, has_previous_page};
use arrayvec::ArrayVec;
use dubp::common::crypto::keys::ed25519::PublicKey;
use dubp::documents::transaction::TransactionDocumentV10;
use dubp::{block::DubpBlockV10, common::crypto::hashs::Hash};
......
......@@ -18,6 +18,8 @@ use duniter_dbs::SourceAmountValV2;
use crate::*;
pub const MAX_FIRST_UTXOS: usize = 40;
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct UtxoCursor {
pub block_number: BlockNumber,
......@@ -58,6 +60,13 @@ impl FromStr for UtxoCursor {
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Utxo {
pub amount: SourceAmount,
pub tx_hash: Hash,
pub output_index: u8,
}
#[derive(Debug, Default)]
pub struct UtxosWithSum {
pub utxos: Vec<(UtxoCursor, SourceAmount)>,
......@@ -169,6 +178,28 @@ impl DbsReader {
data: UtxosWithSum { utxos, sum },
})
}
pub fn first_scripts_utxos(
&self,
first: usize,
scripts: &[WalletScriptV10],
) -> anyhow::Result<Vec<ArrayVec<[Utxo; MAX_FIRST_UTXOS]>>> {
Ok(scripts
.iter()
.map(|script| {
let (k_min, k_max) =
GvaUtxoIdDbV1::script_interval(Hash::compute(script.to_string().as_bytes()));
self.0.gva_utxos().iter(k_min..k_max, |it| {
it.take(first)
.map_ok(|(k, v)| Utxo {
amount: v.0,
tx_hash: k.get_tx_hash(),
output_index: k.get_output_index(),
})
.collect::<KvResult<_>>()
})
})
.collect::<KvResult<Vec<_>>>()?)
}
}
fn find_script_utxos_inner<TxsMpDb, I>(
......@@ -251,8 +282,85 @@ where
mod tests {
use super::*;
use dubp::crypto::keys::PublicKey as _;
use duniter_dbs::databases::txs_mp_v2::TxsMpV2DbWritable;
use duniter_gva_db::GvaV1DbWritable;
use unwrap::unwrap;
#[test]
fn test_first_scripts_utxos() -> anyhow::Result<()> {
let script = WalletScriptV10::single_sig(PublicKey::default());
let script2 = WalletScriptV10::single_sig(unwrap!(PublicKey::from_base58(
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
)));
let gva_db = duniter_gva_db::GvaV1Db::<Mem>::open(MemConf::default())?;
let db_reader = create_dbs_reader(unsafe { std::mem::transmute(&gva_db.get_ro_handler()) });
gva_db.gva_utxos_write().upsert(
GvaUtxoIdDbV1::new(script.clone(), 0, Hash::default(), 0),
SourceAmountValV2(SourceAmount::with_base0(500)),
)?;
gva_db.gva_utxos_write().upsert(
GvaUtxoIdDbV1::new(script.clone(), 1, Hash::default(), 1),
SourceAmountValV2(SourceAmount::with_base0(800)),
)?;
gva_db.gva_utxos_write().upsert(
GvaUtxoIdDbV1::new(script.clone(), 2, Hash::default(), 2),
SourceAmountValV2(SourceAmount::with_base0(1_200)),
)?;
gva_db.gva_utxos_write().upsert(
GvaUtxoIdDbV1::new(script2.clone(), 0, Hash::default(), 0),
SourceAmountValV2(SourceAmount::with_base0(400)),
)?;
gva_db.gva_utxos_write().upsert(
GvaUtxoIdDbV1::new(script2.clone(), 1, Hash::default(), 1),
SourceAmountValV2(SourceAmount::with_base0(700)),
)?;
gva_db.gva_utxos_write().upsert(
GvaUtxoIdDbV1::new(script2.clone(), 2, Hash::default(), 2),
SourceAmountValV2(SourceAmount::with_base0(1_100)),
)?;
assert_eq!(
db_reader.first_scripts_utxos(2, &[script, script2])?,
vec![
[
Utxo {
amount: SourceAmount::with_base0(500),
tx_hash: Hash::default(),
output_index: 0,
},
Utxo {
amount: SourceAmount::with_base0(800),
tx_hash: Hash::default(),
output_index: 1,
},
]
.iter()
.copied()
.collect::<ArrayVec<_>>(),
[
Utxo {
amount: SourceAmount::with_base0(400),
tx_hash: Hash::default(),
output_index: 0,
},
Utxo {
amount: SourceAmount::with_base0(700),
tx_hash: Hash::default(),
output_index: 1,
},
]
.iter()
.copied()
.collect::<ArrayVec<_>>()
]
);
Ok(())
}
#[test]
fn test_find_script_utxos() -> anyhow::Result<()> {
......
......@@ -17,6 +17,7 @@ pub mod block_gva;
pub mod idty_gva;
pub mod tx_gva;
pub mod ud_gva;
pub mod utxos_gva;
use crate::*;
......@@ -116,6 +117,18 @@ pub(crate) struct UtxoGva {
pub(crate) tx_hash: String,
/// Index of output in origin transaction
pub(crate) output_index: u32,
}
#[derive(Clone, async_graphql::SimpleObject)]
pub(crate) struct UtxoTimedGva {
/// Source amount
pub(crate) amount: i64,
/// Source base
pub(crate) base: i64,
/// Hash of origin transaction
pub(crate) tx_hash: String,
/// Index of output in origin transaction
pub(crate) output_index: u32,
/// Written block
pub(crate) written_block: u32,
/// Written time
......
// 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::*;
pub(crate) struct UtxosGva(pub arrayvec::ArrayVec<[UtxoGva; 40]>);
impl async_graphql::Type for UtxosGva {
fn type_name() -> Cow<'static, str> {
Cow::Owned(format!("[{}]", UtxoGva::qualified_type_name()))
}
fn qualified_type_name() -> String {
format!("[{}]!", UtxoGva::qualified_type_name())
}
fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
UtxoGva::create_type_info(registry);
Self::qualified_type_name()
}
}
#[async_trait::async_trait]
impl async_graphql::OutputType for UtxosGva {
async fn resolve(
&self,
ctx: &async_graphql::ContextSelectionSet<'_>,
field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
) -> async_graphql::ServerResult<async_graphql::Value> {
async_graphql::resolver_utils::resolve_list(ctx, field, &self.0, Some(self.0.len())).await
}
}
......@@ -39,8 +39,9 @@ use crate::entities::{
idty_gva::Identity,
tx_gva::TxGva,
ud_gva::{CurrentUdGva, RevalUdGva, UdGva},
utxos_gva::UtxosGva,
AggregateSum, AmountWithBase, EdgeTx, PeerCardGva, RawTxOrChanges, Sum, TxDirection,
TxsHistoryMempool, UtxoGva,
TxsHistoryMempool, UtxoGva, UtxoTimedGva,
};
use crate::inputs::{TxIssuer, TxRecipient, UdsFilter};
use crate::inputs_validators::TxCommentValidator;
......@@ -49,7 +50,7 @@ use crate::scalars::{PkOrScriptGva, PubKeyGva};
#[cfg(test)]
use crate::tests::DbsReader;
use async_graphql::connection::{Connection, Edge, EmptyFields};
use async_graphql::validators::{IntGreaterThan, ListMinLength};
use async_graphql::validators::{IntGreaterThan, IntRange, ListMaxLength, ListMinLength};
use dubp::common::crypto::keys::{ed25519::PublicKey, PublicKey as _};
use dubp::common::prelude::*;
use dubp::crypto::hashs::Hash;
......@@ -66,7 +67,7 @@ use duniter_gva_dbs_reader::DbsReader;
use duniter_mempools::TxsMempool;
use futures::{Stream, StreamExt};
use resiter::map::Map;
use std::{convert::TryFrom, num::NonZeroUsize, ops::Deref};
use std::{borrow::Cow, convert::TryFrom, num::NonZeroUsize, ops::Deref};
#[derive(Clone, Copy, Debug, Default)]
pub struct QueryContext {
......@@ -117,6 +118,11 @@ mod tests {
page_info: PageInfo<duniter_gva_dbs_reader::utxos::UtxoCursor>,
script: &WalletScriptV10,
) -> anyhow::Result<PagedData<duniter_gva_dbs_reader::utxos::UtxosWithSum>>;
fn first_scripts_utxos(
&self,
first: usize,
scripts: &[WalletScriptV10],
) -> anyhow::Result<Vec<arrayvec::ArrayVec<[duniter_gva_dbs_reader::utxos::Utxo; duniter_gva_dbs_reader::utxos::MAX_FIRST_UTXOS]>>>;
fn get_account_balance(
&self,
account_script: &WalletScriptV10,
......
......@@ -17,6 +17,7 @@ pub mod account_balance;
pub mod block;
pub mod current_block;
pub mod current_frame;
pub mod first_utxos_of_scripts;
pub mod gen_tx;
pub mod idty;
pub mod txs_history;
......@@ -33,6 +34,7 @@ pub struct QueryRoot(
queries::block::BlockQuery,
queries::current_block::CurrentBlockQuery,
queries::current_frame::CurrentFrameQuery,
queries::first_utxos_of_scripts::FirstUtxosQuery,
queries::gen_tx::GenTxsQuery,
queries::idty::IdtyQuery,
queries::txs_history::TxsHistoryBlockchainQuery,
......
// 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(Default)]
pub(crate) struct FirstUtxosQuery;
#[async_graphql::Object]
impl FirstUtxosQuery {
/// First utxos of scripts
async fn first_utxos_of_scripts(
&self,
ctx: &async_graphql::Context<'_>,
#[graphql(
desc = "DUBP wallets scripts",
validator(ListMaxLength(length = "100"))
)]
scripts: Vec<PkOrScriptGva>,
#[graphql(
desc = "Number of first utxos to get ",
default = 10,
validator(IntRange(min = "1", max = "40"))
)]
first: i32,
) -> async_graphql::Result<Vec<UtxosGva>> {
let scripts: Vec<WalletScriptV10> = scripts.into_iter().map(|script| script.0).collect();
let data = ctx.data::<GvaSchemaData>()?;
let db_reader = data.dbs_reader();
let utxos_matrice: Vec<arrayvec::ArrayVec<_>> = data
.dbs_pool
.execute(move |_| db_reader.first_scripts_utxos(first as usize, &scripts))
.await??;
Ok(utxos_matrice
.into_iter()
.map(|utxos| {
UtxosGva(
utxos
.into_iter()
.map(|utxo| UtxoGva {
amount: utxo.amount.amount(),
base: utxo.amount.base(),
tx_hash: utxo.tx_hash.to_hex(),
output_index: utxo.output_index as u32,
})
.collect(),
)
})
.collect())
}
}
......@@ -31,7 +31,7 @@ impl UtxosQuery {
#[graphql(desc = "DUBP wallet script")] script: PkOrScriptGva,
#[graphql(desc = "pagination", default)] pagination: Pagination,
#[graphql(desc = "Amount needed")] amount: Option<i64>,
) -> async_graphql::Result<Connection<String, UtxoGva, AggregateSum, EmptyFields>> {
) -> async_graphql::Result<Connection<String, UtxoTimedGva, AggregateSum, EmptyFields>> {
let QueryContext { is_whitelisted } = ctx.data::<QueryContext>()?;
log::info!("is_whitelisted={}", is_whitelisted);
let pagination = Pagination::convert_to_page_info(pagination, *is_whitelisted)?;
......@@ -86,7 +86,7 @@ impl UtxosQuery {
|((utxo_cursor, source_amount), blockchain_time)| {
Edge::new(
utxo_cursor.to_string(),
UtxoGva {
UtxoTimedGva {
amount: source_amount.amount(),
base: source_amount.base(),
tx_hash: utxo_cursor.tx_hash.to_hex(),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment