diff --git a/Cargo.lock b/Cargo.lock index 62fcd0501d66723f7b0ff836208fce95d19f648c..b5b2550d8e0a38ae975f0e3b1d98c7a54e2db300 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1131,6 +1131,7 @@ name = "duniter-gva" version = "0.1.0" dependencies = [ "anyhow", + "arrayvec", "async-graphql", "async-trait", "dubp", @@ -1147,7 +1148,6 @@ dependencies = [ "resiter", "serde", "serde_urlencoded 0.7.0", - "smallvec", "tokio", "unwrap", "warp", diff --git a/rust-libs/duniter-dbs-read-ops/src/find_inputs.rs b/rust-libs/duniter-dbs-read-ops/src/find_inputs.rs index bcdd0a333bf886faad345144b87b9f01316459fe..8bdef91f8c4096f148659a21199e68157669a24f 100644 --- a/rust-libs/duniter-dbs-read-ops/src/find_inputs.rs +++ b/rust-libs/duniter-dbs-read-ops/src/find_inputs.rs @@ -22,7 +22,6 @@ pub fn find_inputs<BcDb: BcV2DbReadable, GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV txs_mp_db: &TxsMpDb, amount: SourceAmount, script: &WalletScriptV10, - max_inputs_count: usize, ) -> KvResult<(BlockMetaV2, Vec<TransactionInputV10>, SourceAmount)> { if let Some(current_block) = crate::get_current_block_meta(bc_db)? { let (mut inputs, uds_sum) = if script.nodes.is_empty() { @@ -38,7 +37,6 @@ pub fn find_inputs<BcDb: BcV2DbReadable, GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV issuer, .., Some(&pending_uds_bn), - Some(max_inputs_count), Some(amount), )?; let inputs = uds @@ -59,13 +57,8 @@ pub fn find_inputs<BcDb: BcV2DbReadable, GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV (vec![], SourceAmount::ZERO) }; if uds_sum < amount { - let (utxos, utxos_sum) = crate::utxos::find_script_utxos( - gva_db, - txs_mp_db, - amount - uds_sum, - max_inputs_count - inputs.len(), - &script, - )?; + let (utxos, utxos_sum) = + crate::utxos::find_script_utxos(gva_db, txs_mp_db, amount - uds_sum, &script)?; inputs.extend( utxos .into_iter() @@ -76,17 +69,9 @@ pub fn find_inputs<BcDb: BcV2DbReadable, GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV }, ), ); - let inputs_sum = uds_sum + utxos_sum; - if inputs_sum < amount { - Err(KvError::Custom( - "Amount need too many sources or issuer's account has an insufficient balance." - .into(), - )) - } else { - Ok::<_, KvError>((current_block, inputs, inputs_sum)) - } + Ok((current_block, inputs, uds_sum + utxos_sum)) } else { - Ok::<_, KvError>((current_block, inputs, uds_sum)) + Ok((current_block, inputs, uds_sum)) } } else { Err(KvError::Custom("no blockchain".into())) @@ -156,7 +141,6 @@ mod tests { &txs_mp_db, SourceAmount::with_base0(55), &script, - 2, )?; assert_eq!(cb, b0); assert_eq!(inputs.len(), 2); @@ -177,7 +161,6 @@ mod tests { &txs_mp_db, SourceAmount::with_base0(55), &script, - 2, )?; assert_eq!(cb, b0); assert_eq!(inputs.len(), 1); diff --git a/rust-libs/duniter-dbs-read-ops/src/uds_of_pubkey.rs b/rust-libs/duniter-dbs-read-ops/src/uds_of_pubkey.rs index 4d5f8ccf97fd10e5f9db54e1af6c0f01827e1ca1..2afc04f696ee20d8201f58de2c88ed4f4ae15b6f 100644 --- a/rust-libs/duniter-dbs-read-ops/src/uds_of_pubkey.rs +++ b/rust-libs/duniter-dbs-read-ops/src/uds_of_pubkey.rs @@ -22,7 +22,6 @@ pub fn uds_of_pubkey<BcDb: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber> pubkey: PublicKey, range: R, bn_to_exclude_opt: Option<&BTreeSet<BlockNumber>>, - limit_opt: Option<usize>, total_opt: Option<SourceAmount>, ) -> KvResult<(Vec<(BlockNumber, SourceAmount)>, SourceAmount)> { let start = match range.start_bound() { @@ -42,7 +41,7 @@ pub fn uds_of_pubkey<BcDb: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber> .collect::<KvResult<Vec<_>>>() })?; if blocks_numbers.is_empty() { - Ok((vec![], SourceAmount::ZERO)) + Ok((Vec::default(), SourceAmount::ZERO)) } else { let first_reval = uds_reval .iter(..=U32BE(blocks_numbers[0].0), |it| { @@ -57,27 +56,29 @@ pub fn uds_of_pubkey<BcDb: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber> true } }); - if let Some(limit) = limit_opt { - collect_uds( - blocks_numbers.take(limit), - blocks_numbers_len, - first_reval, - uds_reval, - total_opt, - ) - } else { - collect_uds( - blocks_numbers, - blocks_numbers_len, - first_reval, - uds_reval, - total_opt, - ) - } + collect_uds( + blocks_numbers, + blocks_numbers_len, + first_reval, + uds_reval, + total_opt, + ) } }) } +macro_rules! collect_one_ud { + ($block_number:ident, $current_ud:ident, $uds:ident, $sum:ident, $amount_target_opt:ident) => { + $uds.push(($block_number, $current_ud)); + $sum = $sum + $current_ud; + if let Some(amount_target) = $amount_target_opt { + if $sum >= amount_target { + return Ok(($uds, $sum)); + } + } + }; +} + fn collect_uds<BC: BackendCol, I: Iterator<Item = BlockNumber>>( mut blocks_numbers: I, blocks_numbers_len: usize, @@ -88,7 +89,7 @@ fn collect_uds<BC: BackendCol, I: Iterator<Item = BlockNumber>>( let uds_revals = uds_reval.iter(first_reval.., |it| it.collect::<KvResult<Vec<_>>>())?; if uds_revals.is_empty() { - Ok((vec![], SourceAmount::ZERO)) + Ok((Vec::default(), SourceAmount::ZERO)) } else { let mut current_ud = (uds_revals[0].1).0; let mut uds = Vec::with_capacity(blocks_numbers_len); @@ -99,37 +100,63 @@ fn collect_uds<BC: BackendCol, I: Iterator<Item = BlockNumber>>( 'blocks_numbers: while let Some(block_number) = blocks_numbers.next() { if block_number.0 >= block_reval.0 { current_ud = amount_reval.0; - uds.push((block_number, current_ud)); - sum = sum + current_ud; - if let Some(amount_target) = amount_opt { - if sum >= amount_target { - return Ok((uds, sum)); - } - } + collect_one_ud!(block_number, current_ud, uds, sum, amount_opt); break 'blocks_numbers; } else { - uds.push((block_number, current_ud)); - sum = sum + current_ud; - if let Some(amount_target) = amount_opt { - if sum >= amount_target { - return Ok((uds, sum)); - } - } + collect_one_ud!(block_number, current_ud, uds, sum, amount_opt); } } } // Uds after last reval for block_number in blocks_numbers { - uds.push((block_number, current_ud)); - sum = sum + current_ud; - if let Some(amount_target) = amount_opt { - if sum >= amount_target { - return Ok((uds, sum)); - } - } + collect_one_ud!(block_number, current_ud, uds, sum, amount_opt); } Ok((uds, sum)) } } + +#[cfg(test)] +mod tests { + + use super::*; + use duniter_dbs::{bc_v2::BcV2DbWritable, SourceAmountValV2, UdIdV2}; + + #[test] + fn test_uds_of_pubkey() -> KvResult<()> { + let bc_db = duniter_dbs::bc_v2::BcV2Db::<Mem>::open(MemConf::default())?; + + let pk = PublicKey::default(); + + bc_db + .uds_reval_write() + .upsert(U32BE(0), SourceAmountValV2(SourceAmount::with_base0(10)))?; + bc_db + .uds_reval_write() + .upsert(U32BE(40), SourceAmountValV2(SourceAmount::with_base0(12)))?; + + bc_db.uds_write().upsert(UdIdV2(pk, BlockNumber(0)), ())?; + bc_db.uds_write().upsert(UdIdV2(pk, BlockNumber(10)), ())?; + bc_db.uds_write().upsert(UdIdV2(pk, BlockNumber(20)), ())?; + bc_db.uds_write().upsert(UdIdV2(pk, BlockNumber(30)), ())?; + bc_db.uds_write().upsert(UdIdV2(pk, BlockNumber(40)), ())?; + bc_db.uds_write().upsert(UdIdV2(pk, BlockNumber(50)), ())?; + bc_db.uds_write().upsert(UdIdV2(pk, BlockNumber(60)), ())?; + + // Get all uds + let (uds, uds_sum) = uds_of_pubkey(&bc_db, pk, .., None, None)?; + assert_eq!(uds.len(), 7); + assert_eq!( + uds.first(), + Some(&(BlockNumber(0), SourceAmount::with_base0(10))) + ); + assert_eq!( + uds.last(), + Some(&(BlockNumber(60), SourceAmount::with_base0(12))) + ); + assert_eq!(uds_sum, SourceAmount::with_base0(76)); + + Ok(()) + } +} diff --git a/rust-libs/duniter-dbs-read-ops/src/utxos.rs b/rust-libs/duniter-dbs-read-ops/src/utxos.rs index d77f6d60097de7711b05c60483aa0a0587a81343..2b12831383cc7ca9e2395861be70f4d0a40ec844 100644 --- a/rust-libs/duniter-dbs-read-ops/src/utxos.rs +++ b/rust-libs/duniter-dbs-read-ops/src/utxos.rs @@ -25,7 +25,7 @@ pub fn get_script_utxos<GvaDb: GvaV1DbReadable>( script: &WalletScriptV10, ) -> KvResult<(Vec<UtxoV10>, SourceAmount)> { find_script_utxos_inner::<_, duniter_dbs::txs_mp_v2::TxsMpV2DbRo<Mem>>( - gva_db_ro, None, script, None, None, + gva_db_ro, None, script, None, ) } @@ -33,30 +33,21 @@ pub fn find_script_utxos<GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV2DbReadable>( gva_db_ro: &GvaDb, txs_mp_db_ro: &TxsMpDb, amount: SourceAmount, - limit: usize, script: &WalletScriptV10, ) -> KvResult<(Vec<UtxoV10>, SourceAmount)> { - find_script_utxos_inner( - gva_db_ro, - Some(txs_mp_db_ro), - script, - Some(limit), - Some(amount), - ) + find_script_utxos_inner(gva_db_ro, Some(txs_mp_db_ro), script, Some(amount)) } fn find_script_utxos_inner<GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV2DbReadable>( gva_db_ro: &GvaDb, txs_mp_db_ro: Option<&TxsMpDb>, script: &WalletScriptV10, - limit_opt: Option<usize>, total_opt: Option<SourceAmount>, ) -> KvResult<(Vec<UtxoV10>, SourceAmount)> { if let Some(utxos_of_script) = gva_db_ro .utxos_by_script() .get(&WalletConditionsV2::from_ref(script))? { - let mut count = 0; let mut total = SourceAmount::ZERO; let mut utxos: Vec<(i64, UtxoIdV10, SourceAmount)> = Vec::with_capacity(utxos_of_script.0.len() * 2); @@ -73,12 +64,6 @@ fn find_script_utxos_inner<GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV2DbReadable>( { utxos.push((written_time, utxo_id, source_amount)); total = total + source_amount; - if let Some(limit) = limit_opt { - count += 1; - if count == limit { - return Ok((utxos, total)); - } - } if let Some(total_target) = total_opt { if total >= total_target { return Ok((utxos, total)); diff --git a/rust-libs/modules/duniter-gva/Cargo.toml b/rust-libs/modules/duniter-gva/Cargo.toml index 7d252b7d1153292323daadb92806095a2f268f97..015bdf8d4b32310dac0017c3c887d0b17cbf91ef 100644 --- a/rust-libs/modules/duniter-gva/Cargo.toml +++ b/rust-libs/modules/duniter-gva/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] anyhow = "1.0.33" +arrayvec = "0.5.1" async-graphql = "2.0.0" async-trait = "0.1.41" dubp = { version = "0.31.0" } @@ -23,7 +24,6 @@ log = "0.4.11" resiter = "0.4.0" serde = { version = "1.0.105", features = ["derive"] } serde_urlencoded = "0.7.0" -smallvec = { version = "1.4.0", features = ["serde", "write"] } tokio = { version = "0.2.22", features = ["io-util", "rt-threaded"] } warp = "0.2" diff --git a/rust-libs/modules/duniter-gva/src/lib.rs b/rust-libs/modules/duniter-gva/src/lib.rs index e680ee61689e22b1b7923ec21759363a468b8de2..a40daf97d9d7bb4f435029576aa64490297ead7f 100644 --- a/rust-libs/modules/duniter-gva/src/lib.rs +++ b/rust-libs/modules/duniter-gva/src/lib.rs @@ -41,10 +41,7 @@ use async_graphql::http::GraphQLPlaygroundConfig; use dubp::common::crypto::keys::{ed25519::PublicKey, KeyPair as _, PublicKey as _}; use dubp::common::prelude::*; use dubp::documents::prelude::*; -use dubp::documents::transaction::{ - TransactionDocumentTrait, TransactionDocumentV10, TransactionDocumentV10Builder, - TransactionInputUnlocksV10, TransactionInputV10, TransactionOutputV10, UTXOConditions, -}; +use dubp::documents::transaction::{TransactionDocumentTrait, TransactionDocumentV10}; use dubp::documents_parser::prelude::*; use dubp::wallet::prelude::*; use duniter_dbs::prelude::*; @@ -52,7 +49,6 @@ use duniter_dbs::{kv_typed::prelude::*, TxDbV2, TxsMpV2DbReadable}; use duniter_mempools::{Mempools, TxsMempool}; use futures::{StreamExt, TryStreamExt}; use resiter::map::Map; -use smallvec::smallvec as svec; use std::convert::Infallible; use std::ops::Deref; use warp::{http::Response as HttpResponse, Filter as _, Rejection, Stream}; diff --git a/rust-libs/modules/duniter-gva/src/queries/gen_txs.rs b/rust-libs/modules/duniter-gva/src/queries/gen_txs.rs index df736ad0fe539d78be7c39d24f7cc2d86f196ee7..515aa170cf3451e3995730e013ac6aa0bb24dd02 100644 --- a/rust-libs/modules/duniter-gva/src/queries/gen_txs.rs +++ b/rust-libs/modules/duniter-gva/src/queries/gen_txs.rs @@ -15,6 +15,8 @@ use crate::*; +const MAX_INPUTS_PER_TX: usize = 40; + #[derive(Default)] pub(crate) struct GenTxsQuery; #[async_graphql::Object] @@ -48,75 +50,27 @@ impl GenTxsQuery { &dbs.txs_mp_db, amount, &WalletScriptV10::single(WalletConditionV10::Sig(issuer)), - 40, ) }) .await??; + if inputs_sum < amount { + return Err(async_graphql::Error::new("insufficient balance")); + } + let current_blockstamp = Blockstamp { number: BlockNumber(current_block.number), hash: BlockHash(current_block.hash), }; - Ok(vec![gen_tx_with_inputs( - amount, + Ok(TransactionDocumentV10::generate_simple_txs( current_blockstamp, - comment, currency, (inputs, inputs_sum), + MAX_INPUTS_PER_TX, issuer, recipient, - )]) - } -} - -fn gen_tx_with_inputs( - amount: SourceAmount, - blockstamp: Blockstamp, - comment: String, - currency: String, - inputs_with_sum: (Vec<TransactionInputV10>, SourceAmount), - issuer: PublicKey, - recipient: PublicKey, -) -> String { - let (inputs, inputs_sum) = inputs_with_sum; - let inputs_len = inputs.len(); - let unlocks = (0..inputs_len) - .into_iter() - .map(TransactionInputUnlocksV10::single_index) - .collect::<Vec<_>>(); - - let rest = inputs_sum - amount; - let main_output = TransactionOutputV10 { - amount, - conditions: UTXOConditions::from(WalletScriptV10::single(WalletConditionV10::Sig( - recipient, - ))), - }; - let outputs = if rest.amount() > 0 { - svec![ - main_output, - TransactionOutputV10 { - amount: rest, - conditions: UTXOConditions::from(WalletScriptV10::single(WalletConditionV10::Sig( - issuer, - ))), - }, - ] - } else { - svec![main_output] - }; - - TransactionDocumentV10Builder { - currency: ¤cy, - blockstamp, - locktime: 0, - issuers: svec![issuer], - inputs: &inputs, - unlocks: &unlocks, - outputs, - comment: &comment, - hash: None, + (amount, comment), + )) } - .generate_text() } diff --git a/rust-libs/modules/duniter-gva/src/queries/uds.rs b/rust-libs/modules/duniter-gva/src/queries/uds.rs index bc36f3b8f688ad7dba026256dca2c1a4ec857931..7cab2002838c19e1422775a411d671e046b9b29e 100644 --- a/rust-libs/modules/duniter-gva/src/queries/uds.rs +++ b/rust-libs/modules/duniter-gva/src/queries/uds.rs @@ -55,7 +55,6 @@ impl UdsQuery { .., None, None, - None, ) }) .await??;