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

[feat] gva:gen_tx: exclude inputs in mempool on inputs search

parent b25d8960
Branches
No related tags found
1 merge request!1335Gva proto 2
Showing
with 467 additions and 152 deletions
......@@ -1110,6 +1110,7 @@ dependencies = [
"dubp",
"duniter-dbs",
"resiter",
"smallvec",
]
[[package]]
......
......@@ -15,3 +15,6 @@ path = "src/lib.rs"
duniter-dbs = { path = "../duniter-dbs" }
dubp = { version = "0.30.0" }
resiter = "0.4.0"
[dev-dependencies]
smallvec = { version = "1.4.0", features = ["serde", "write"] }
// 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 dubp::{documents::transaction::TransactionInputV10, wallet::prelude::*};
pub fn find_inputs<BcDb: BcV2DbReadable, GvaDb: GvaV1DbReadable, TxsMpDb: TxsMpV2DbReadable>(
bc_db: &BcDb,
gva_db: &GvaDb,
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() {
if let WalletSubScriptV10::Single(WalletConditionV10::Sig(issuer)) = script.root {
let pending_uds_bn = txs_mp_db.uds_ids().iter(.., |it| {
it.keys()
.map_ok(|duniter_dbs::UdIdV2(_pk, bn)| bn)
.collect::<KvResult<_>>()
})?;
let (uds, uds_sum) = crate::uds_of_pubkey::uds_of_pubkey(
bc_db,
issuer,
..,
Some(&pending_uds_bn),
Some(max_inputs_count),
Some(amount),
)?;
let inputs = uds
.into_iter()
.map(|(block_number, source_amount)| TransactionInputV10 {
amount: source_amount,
id: SourceIdV10::Ud(UdSourceIdV10 {
issuer,
block_number,
}),
})
.collect::<Vec<_>>();
(inputs, uds_sum)
} else {
(vec![], SourceAmount::ZERO)
}
} else {
(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,
)?;
inputs.extend(
utxos
.into_iter()
.map(
|(_written_time, utxo_id, source_amount)| TransactionInputV10 {
amount: source_amount,
id: SourceIdV10::Utxo(utxo_id),
},
),
);
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))
}
} else {
Ok::<_, KvError>((current_block, inputs, uds_sum))
}
} else {
Err(KvError::Custom("no blockchain".into()))
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::*;
use duniter_dbs::{
bc_v2::BcV2DbWritable, gva_v1::GvaV1DbWritable, txs_mp_v2::TxsMpV2DbWritable,
SourceAmountValV2, UdIdV2, UtxoIdDbV2, UtxosOfScriptV1, WalletConditionsV2,
};
const UD0: i64 = 10;
#[test]
fn test_find_inputs() -> KvResult<()> {
let bc_db = duniter_dbs::bc_v2::BcV2Db::<Mem>::open(MemConf::default())?;
let gva_db = duniter_dbs::gva_v1::GvaV1Db::<Mem>::open(MemConf::default())?;
let txs_mp_db = duniter_dbs::txs_mp_v2::TxsMpV2Db::<Mem>::open(MemConf::default())?;
let b0 = BlockMetaV2 {
dividend: Some(SourceAmount::with_base0(UD0)),
..Default::default()
};
let pk = PublicKey::default();
let script = WalletScriptV10::single(WalletConditionV10::Sig(pk));
let mut utxos = BTreeMap::new();
utxos.insert(
0,
smallvec::smallvec![
(
UtxoIdV10 {
tx_hash: Hash::default(),
output_index: 0
},
SourceAmount::with_base0(50)
),
(
UtxoIdV10 {
tx_hash: Hash::default(),
output_index: 1
},
SourceAmount::with_base0(80)
)
],
);
bc_db.blocks_meta_write().upsert(U32BE(0), b0)?;
bc_db
.uds_reval_write()
.upsert(U32BE(0), SourceAmountValV2(SourceAmount::with_base0(UD0)))?;
bc_db
.uds_write()
.upsert(UdIdV2(PublicKey::default(), BlockNumber(0)), ())?;
gva_db
.utxos_by_script_write()
.upsert(WalletConditionsV2(script.clone()), UtxosOfScriptV1(utxos))?;
// Gen tx1
let (cb, inputs, inputs_sum) = find_inputs(
&bc_db,
&gva_db,
&txs_mp_db,
SourceAmount::with_base0(55),
&script,
2,
)?;
assert_eq!(cb, b0);
assert_eq!(inputs.len(), 2);
assert_eq!(inputs_sum, SourceAmount::with_base0(60));
// Insert tx1 inputs in mempool
txs_mp_db
.uds_ids_write()
.upsert(UdIdV2(pk, BlockNumber(0)), ())?;
txs_mp_db
.utxos_ids_write()
.upsert(UtxoIdDbV2(Hash::default(), 0), ())?;
// Gen tx2
let (cb, inputs, inputs_sum) = find_inputs(
&bc_db,
&gva_db,
&txs_mp_db,
SourceAmount::with_base0(55),
&script,
2,
)?;
assert_eq!(cb, b0);
assert_eq!(inputs.len(), 1);
assert_eq!(inputs_sum, SourceAmount::with_base0(80));
Ok(())
}
}
......@@ -22,6 +22,7 @@
unused_import_braces
)]
pub mod find_inputs;
pub mod txs_history;
pub mod uds_of_pubkey;
pub mod utxos;
......@@ -36,7 +37,10 @@ use duniter_dbs::{
TxsMpV2DbReadable,
};
use resiter::map::Map;
use std::ops::{Bound, RangeBounds};
use std::{
collections::BTreeSet,
ops::{Bound, RangeBounds},
};
pub fn get_current_block_meta<BcDb: BcV2DbReadable>(bc_db: &BcDb) -> KvResult<Option<BlockMetaV2>> {
bc_db
......
......@@ -17,10 +17,11 @@ use crate::*;
use duniter_dbs::bc_v2::UdsRevalEvent;
use duniter_dbs::UdIdV2;
pub fn uds_of_pubkey<DB: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber>>(
bc_db: &DB,
pub fn uds_of_pubkey<BcDb: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber>>(
bc_db: &BcDb,
pubkey: PublicKey,
range: R,
bn_to_exclude_opt: Option<&BTreeSet<BlockNumber>>,
limit_opt: Option<usize>,
total_opt: Option<SourceAmount>,
) -> KvResult<(Vec<(BlockNumber, SourceAmount)>, SourceAmount)> {
......@@ -48,23 +49,38 @@ pub fn uds_of_pubkey<DB: BcV2DbReadable, R: 'static + RangeBounds<BlockNumber>>(
it.reverse().keys().next_res()
})?
.expect("corrupted db");
let blocks_numbers = blocks_numbers.into_iter();
let blocks_numbers_len = blocks_numbers.len();
let blocks_numbers = blocks_numbers.into_iter().filter(|bn| {
if let Some(bn_to_exclude) = bn_to_exclude_opt {
!bn_to_exclude.contains(bn)
} else {
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, first_reval, uds_reval, total_opt)
collect_uds(
blocks_numbers,
blocks_numbers_len,
first_reval,
uds_reval,
total_opt,
)
}
}
})
}
fn collect_uds<BC: BackendCol, I: ExactSizeIterator<Item = BlockNumber>>(
fn collect_uds<BC: BackendCol, I: Iterator<Item = BlockNumber>>(
mut blocks_numbers: I,
blocks_numbers_len: usize,
first_reval: U32BE,
uds_reval: TxColRo<BC, UdsRevalEvent>,
amount_opt: Option<SourceAmount>,
......@@ -75,7 +91,7 @@ fn collect_uds<BC: BackendCol, I: ExactSizeIterator<Item = BlockNumber>>(
Ok((vec![], SourceAmount::ZERO))
} else {
let mut current_ud = (uds_revals[0].1).0;
let mut uds = Vec::with_capacity(blocks_numbers.len());
let mut uds = Vec::with_capacity(blocks_numbers_len);
let mut sum = SourceAmount::ZERO;
// Uds before last reval
......
......@@ -23,6 +23,32 @@ pub type UtxoV10 = (i64, UtxoIdV10, SourceAmount);
pub fn get_script_utxos<GvaDb: GvaV1DbReadable>(
gva_db_ro: &GvaDb,
script: &WalletScriptV10,
) -> KvResult<(Vec<UtxoV10>, SourceAmount)> {
find_script_utxos_inner::<_, duniter_dbs::txs_mp_v2::TxsMpV2DbRo<Mem>>(
gva_db_ro, None, script, None, None,
)
}
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),
)
}
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)> {
......@@ -36,14 +62,23 @@ pub fn get_script_utxos<GvaDb: GvaV1DbReadable>(
Vec::with_capacity(utxos_of_script.0.len() * 2);
for (written_time, utxos_) in utxos_of_script.0 {
for (utxo_id, source_amount) in utxos_ {
if txs_mp_db_ro.is_none()
|| !txs_mp_db_ro
.expect("unreachable")
.utxos_ids()
.contains_key(&duniter_dbs::UtxoIdDbV2(
utxo_id.tx_hash,
utxo_id.output_index as u32,
))?
{
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));
}
}
total = total + source_amount;
if let Some(total_target) = total_opt {
if total >= total_target {
return Ok((utxos, total));
......@@ -51,6 +86,7 @@ pub fn get_script_utxos<GvaDb: GvaV1DbReadable>(
}
}
}
}
Ok((utxos, total))
} else {
Ok((vec![], SourceAmount::ZERO))
......
......@@ -55,9 +55,18 @@ pub fn add_pending_tx<
txs_mp_db.txs_by_issuer_write(),
txs_mp_db.txs_by_recipient_write(),
txs_mp_db.txs_write(),
txs_mp_db.uds_ids_write(),
txs_mp_db.utxos_ids_write(),
)
.write(
|(mut txs_by_recv_time, mut txs_by_issuer, mut txs_by_recipient, mut txs)| {
|(
mut txs_by_recv_time,
mut txs_by_issuer,
mut txs_by_recipient,
mut txs,
mut uds_ids,
mut utxos_ids,
)| {
control(&tx, &txs)?;
// Insert on col `txs_by_recv_time`
let mut hashs = txs_by_recv_time.get(&received_time)?.unwrap_or_default();
......@@ -77,6 +86,20 @@ pub fn add_pending_tx<
hashs.insert(tx.get_hash());
txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs);
}
// Insert tx inputs in cols `uds_ids` and `utxos_ids`
for input in tx.get_inputs() {
match input.id {
SourceIdV10::Ud(UdSourceIdV10 {
issuer,
block_number,
}) => uds_ids.upsert(duniter_dbs::UdIdV2(issuer, block_number), ()),
SourceIdV10::Utxo(UtxoIdV10 {
tx_hash,
output_index,
}) => utxos_ids
.upsert(duniter_dbs::UtxoIdDbV2(tx_hash, output_index as u32), ()),
}
}
// Insert tx itself
txs.upsert(HashKeyV2(tx_hash), PendingTxDbV2(tx.into_owned()));
Ok(())
......@@ -89,6 +112,8 @@ pub fn remove_all_pending_txs<B: Backend>(txs_mp_db: &TxsMpV2Db<B>) -> KvResult<
txs_mp_db.txs_by_issuer_write().clear()?;
txs_mp_db.txs_by_recipient_write().clear()?;
txs_mp_db.txs_write().clear()?;
txs_mp_db.uds_ids_write().clear()?;
txs_mp_db.utxos_ids_write().clear()?;
Ok(())
}
......@@ -128,11 +153,29 @@ fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) ->
txs_mp_db.txs_by_issuer_write(),
txs_mp_db.txs_by_recipient_write(),
txs_mp_db.txs_write(),
txs_mp_db.uds_ids_write(),
txs_mp_db.utxos_ids_write(),
)
.write(|(mut txs_by_issuer, mut txs_by_recipient, mut txs)| {
.write(
|(mut txs_by_issuer, mut txs_by_recipient, mut txs, mut uds_ids, mut utxos_ids)| {
// Remove tx inputs in cols `uds_ids` and `utxos_ids`
for input in tx.0.get_inputs() {
match input.id {
SourceIdV10::Ud(UdSourceIdV10 {
issuer,
block_number,
}) => uds_ids.remove(duniter_dbs::UdIdV2(issuer, block_number)),
SourceIdV10::Utxo(UtxoIdV10 {
tx_hash,
output_index,
}) => utxos_ids
.remove(duniter_dbs::UtxoIdDbV2(tx_hash, output_index as u32)),
}
}
// Remove tx hash in col `txs_by_issuer`
for pubkey in tx.0.issuers() {
let mut hashs_ = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
let mut hashs_ =
txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
hashs_.remove(&tx_hash);
txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs_)
}
......@@ -147,7 +190,8 @@ fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) ->
// Remove tx itself
txs.remove(HashKeyV2(tx_hash));
Ok(true)
})
},
)
} else {
Ok(false)
}
......
......@@ -23,4 +23,5 @@ pub mod source_key;
pub mod timestamp;
pub mod ud_id;
pub mod uid;
pub mod utxo_id;
pub mod wallet_conditions;
......@@ -61,51 +61,6 @@ impl ExplorableKey for BlockNumberKeyV1 {
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct BlockNumberKeyV2(pub BlockNumber);
impl KeyAsBytes for BlockNumberKeyV2 {
fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T {
f(&(self.0).0.to_be_bytes()[..])
}
}
impl FromBytes for BlockNumberKeyV2 {
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
Ok(Self(BlockNumber(
zerocopy::LayoutVerified::<_, zerocopy::U32<byteorder::BigEndian>>::new(bytes)
.ok_or_else(|| {
StringErr(
"Corrupted DB: BlockNumber bytes are invalid length or unaligned"
.to_owned(),
)
})?
.get(),
)))
}
}
impl ToDumpString for BlockNumberKeyV2 {
fn to_dump_string(&self) -> String {
todo!()
}
}
#[cfg(feature = "explorer")]
impl ExplorableKey for BlockNumberKeyV2 {
fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
Ok(Self(
BlockNumber::from_str(source).map_err(|e| StringErr(format!("{}", e)))?,
))
}
fn to_explorer_string(&self) -> KvResult<String> {
Ok(format!("{}", (self.0).0))
}
}
#[cfg(test)]
mod tests {
......@@ -116,12 +71,4 @@ mod tests {
BlockNumberKeyV1(BlockNumber(35))
.as_bytes(|bytes| assert_eq!(bytes, &[48, 48, 48, 48, 48, 48, 48, 48, 51, 53]))
}
#[test]
fn block_number_key_v2() {
let k = BlockNumberKeyV2(BlockNumber(3));
k.as_bytes(|bytes| {
assert_eq!(bytes, &[0, 0, 0, 3]);
});
}
}
// 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 uninit::prelude::*;
type OutputIndex = u32;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct UtxoIdDbV2(pub Hash, pub OutputIndex);
impl PartialOrd for UtxoIdDbV2 {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match self.0.partial_cmp(&other.0) {
Some(std::cmp::Ordering::Equal) => self.1.partial_cmp(&other.1),
o => o,
}
}
}
impl Ord for UtxoIdDbV2 {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.0.cmp(&other.0) {
std::cmp::Ordering::Equal => self.1.cmp(&other.1),
o => o,
}
}
}
impl KeyAsBytes for UtxoIdDbV2 {
fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T {
let mut buffer = uninit_array![u8; 36];
let (hash_buffer, index_buffer) = buffer.as_out().split_at_out(32);
let hash_buffer = hash_buffer.copy_from_slice(self.0.as_ref());
index_buffer.copy_from_slice(&(self.1).to_be_bytes());
f(unsafe { std::slice::from_raw_parts_mut(hash_buffer.as_mut_ptr(), 36) })
}
}
impl FromBytes for UtxoIdDbV2 {
type Err = StringErr;
fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
let hash = zerocopy::LayoutVerified::<_, Hash>::new(&bytes[..32]).ok_or_else(|| {
StringErr("Corrupted DB: Hash bytes are invalid length or unaligned".to_owned())
})?;
let output_index =
zerocopy::LayoutVerified::<_, zerocopy::U32<byteorder::BigEndian>>::new(&bytes[32..])
.ok_or_else(|| {
StringErr(
"Corrupted DB: OutputIndex bytes are invalid length or unaligned"
.to_owned(),
)
})?
.get();
Ok(UtxoIdDbV2(*hash, output_index))
}
}
impl ToDumpString for UtxoIdDbV2 {
fn to_dump_string(&self) -> String {
todo!()
}
}
#[cfg(feature = "explorer")]
impl ExplorableKey for UtxoIdDbV2 {
fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
let mut source = source.split(':');
if let Some(hash_str) = source.next() {
let hash =
Hash::from_hex(&hash_str).map_err(|e| StringErr(format!("{}: {}", e, hash_str)))?;
if let Some(output_index_str) = source.next() {
Ok(UtxoIdDbV2(
hash,
u32::from_str(output_index_str).map_err(|e| StringErr(format!("{}", e)))?,
))
} else {
Err(StringErr("UtxoIdDbV2: Invalid format".to_owned()))
}
} else {
Err(StringErr("UtxoIdDbV2: Invalid format".to_owned()))
}
}
fn to_explorer_string(&self) -> KvResult<String> {
Ok(format!("{}:{}", self.0.to_hex(), self.1))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn utxo_id_v2_as_bytes() -> std::result::Result<(), StringErr> {
let utxo_id = UtxoIdDbV2(Hash::default(), 3);
let utxo_id_2_res = utxo_id.as_bytes(|bytes| {
assert_eq!(
bytes,
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 3
]
);
UtxoIdDbV2::from_bytes(bytes)
});
assert_eq!(utxo_id_2_res?, utxo_id);
Ok(())
}
}
......@@ -58,6 +58,7 @@ pub use crate::errors::Result;
pub use crate::open_dbs::open_dbs;
// Export profession types
pub use crate::keys::utxo_id::UtxoIdDbV2;
pub use bc_v1::{BcV1Db, BcV1DbReadable, BcV1DbRo, BcV1DbWritable, MainBlocksEvent, UidsEvent};
pub use gva_v1::{GvaV1Db, GvaV1DbReadable, GvaV1DbRo, GvaV1DbWritable};
pub use keys::all::AllKeyV1;
......
......@@ -22,5 +22,7 @@ db_schema!(
["txs_by_issuer", TxsByIssuer, PubKeyKeyV2, BTreeSet<Hash>],
["txs_by_recipient", TxsByRecipient, PubKeyKeyV2, BTreeSet<Hash>],
["txs_by_received_time", TxsByRecvTime, i64, BTreeSet<Hash>],
["uds_ids", UdsIds, UdIdV2, ()],
["utxos_ids", UtxosIds, UtxoIdDbV2, ()],
]
);
......@@ -15,9 +15,6 @@
use crate::*;
const MAX_UDS_INPUTS: usize = 20;
const MAX_UTXOS_INPUTS: usize = 20;
#[derive(Default)]
pub(crate) struct GenTxsQuery;
#[async_graphql::Object]
......@@ -45,62 +42,14 @@ impl GenTxsQuery {
let (current_block, inputs, inputs_sum) = data
.dbs_pool
.execute(move |dbs| {
if let Some(current_block) =
duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db)?
{
let (uds, uds_sum) = duniter_dbs_read_ops::uds_of_pubkey::uds_of_pubkey(
duniter_dbs_read_ops::find_inputs::find_inputs(
&dbs.bc_db,
issuer,
..,
Some(MAX_UDS_INPUTS),
Some(amount),
)?;
let mut inputs = uds
.into_iter()
.map(
|(block_number, source_amount)| TransactionInputV10 {
amount: source_amount,
id: SourceIdV10::Ud(UdSourceIdV10 {
issuer,
block_number,
}),
},
)
.collect::<Vec<_>>();
if uds_sum < amount {
let (utxos, utxos_sum) = duniter_dbs_read_ops::utxos::get_script_utxos(
&dbs.gva_db,
&dbs.txs_mp_db,
amount,
&WalletScriptV10::single(WalletConditionV10::Sig(issuer)),
Some(MAX_UTXOS_INPUTS),
Some(amount - uds_sum),
)?;
inputs.extend(utxos.into_iter()
.map(
|(_written_time, utxo_id, source_amount)| TransactionInputV10 {
amount: source_amount,
id: SourceIdV10::Utxo(utxo_id),
},
));
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
))
}
} else {
Ok::<_, KvError>((
current_block,
inputs,
uds_sum
))
}
} else {
Err(KvError::Custom("no blockchain".into()))
}
40,
)
})
.await??;
......
......@@ -55,6 +55,7 @@ impl UdsQuery {
..,
None,
None,
None,
)
})
.await??;
......
......@@ -36,9 +36,7 @@ impl UtxosQuery {
let (utxos, _balance) = data
.dbs_pool
.execute(move |dbs| {
duniter_dbs_read_ops::utxos::get_script_utxos(&dbs.gva_db, &script, None, None)
})
.execute(move |dbs| duniter_dbs_read_ops::utxos::get_script_utxos(&dbs.gva_db, &script))
.await??;
let utxos: Vec<UtxoGva> = utxos
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment