diff --git a/Cargo.lock b/Cargo.lock index f9ec290968ec6de3070b3bea1cf4d7f70a351aae..0e7f2852babea8883e990ed6c9bd3b4238f94286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1162,6 +1162,7 @@ dependencies = [ "duniter-dbs", "duniter-gva-db", "duniter-gva-dbs-reader", + "duniter-mempools", "fast-threadpool", "futures", "mockall", @@ -1178,6 +1179,7 @@ dependencies = [ "bincode", "dubp", "serde", + "smallvec", "thiserror", ] diff --git a/rust-libs/modules/gva/bca/Cargo.toml b/rust-libs/modules/gva/bca/Cargo.toml index f4f6b1749eba6012060814430a98ec4f9ebee5fe..1495e533efb813c15cb85e8117e48e21b0371679 100644 --- a/rust-libs/modules/gva/bca/Cargo.toml +++ b/rust-libs/modules/gva/bca/Cargo.toml @@ -15,6 +15,7 @@ duniter-bca-types = { path = "types", features = ["duniter"] } duniter-dbs = { path = "../../../duniter-dbs" } duniter-gva-db = { path = "../db" } duniter-gva-dbs-reader = { path = "../dbs-reader" } +duniter-mempools = { path = "../../../duniter-mempools" } fast-threadpool = "0.2.3" futures = "0.3.6" once_cell = "1.5" diff --git a/rust-libs/modules/gva/bca/src/exec_req_type.rs b/rust-libs/modules/gva/bca/src/exec_req_type.rs index 3134f3d3ef1037a86293701d3124686c4299ddff..bf8e8e25568fcd4993389acd29fb0541200d3c1e 100644 --- a/rust-libs/modules/gva/bca/src/exec_req_type.rs +++ b/rust-libs/modules/gva/bca/src/exec_req_type.rs @@ -15,6 +15,7 @@ mod members_count; mod prepare_simple_payment; +mod send_txs; use dubp::crypto::keys::KeyPair; @@ -51,5 +52,6 @@ pub(super) async fn execute_req_type( .sign(&challenge), }), BcaReqTypeV0::Ping => Ok(BcaRespTypeV0::Pong), + BcaReqTypeV0::SendTxs(txs) => send_txs::send_txs(bca_executor, txs).await, } } diff --git a/rust-libs/modules/gva/bca/src/exec_req_type/send_txs.rs b/rust-libs/modules/gva/bca/src/exec_req_type/send_txs.rs new file mode 100644 index 0000000000000000000000000000000000000000..e6a5c25c4aa77ba41ddfa1ad189d15ece2f53c3f --- /dev/null +++ b/rust-libs/modules/gva/bca/src/exec_req_type/send_txs.rs @@ -0,0 +1,63 @@ +// 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::{crypto::keys::KeyPair, documents::transaction::TransactionDocumentTrait}; +use duniter_bca_types::{ + rejected_tx::{RejectedTx, RejectedTxReason}, + Txs, +}; + +pub(super) async fn send_txs( + bca_executor: &BcaExecutor, + txs: Txs, +) -> Result<BcaRespTypeV0, ExecReqTypeError> { + let expected_currency = bca_executor.currency.clone(); + + let server_pubkey = bca_executor.self_keypair.public_key(); + let txs_mempool = bca_executor.txs_mempool; + + let mut rejected_txs = Vec::new(); + for (i, tx) in txs.into_iter().enumerate() { + if let Err(e) = tx.verify(Some(&expected_currency)) { + rejected_txs.push(RejectedTx { + tx_index: i as u16, + reason: RejectedTxReason::InvalidTx(e.to_string()), + }); + } else if let Err(rejected_tx) = bca_executor + .dbs_pool + .execute(move |dbs| { + txs_mempool + .add_pending_tx(&dbs.bc_db_ro, server_pubkey, &dbs.txs_mp_db, &tx) + .map_err(|e| RejectedTx { + tx_index: i as u16, + reason: match e { + duniter_mempools::TxMpError::Db(e) => { + RejectedTxReason::DbError(e.to_string()) + } + duniter_mempools::TxMpError::Full => RejectedTxReason::MempoolFull, + duniter_mempools::TxMpError::TxAlreadyWritten => { + RejectedTxReason::TxAlreadyWritten + } + }, + }) + }) + .await? + { + rejected_txs.push(rejected_tx); + } + } + Ok(BcaRespTypeV0::RejectedTxs(rejected_txs)) +} diff --git a/rust-libs/modules/gva/bca/src/lib.rs b/rust-libs/modules/gva/bca/src/lib.rs index 91a8ba1b6bf5f3602cc6148c9a6c1900f868f11c..d031c7b0bd82f2f28b0e7aa42b713395d3a4dff3 100644 --- a/rust-libs/modules/gva/bca/src/lib.rs +++ b/rust-libs/modules/gva/bca/src/lib.rs @@ -54,6 +54,7 @@ pub fn set_bca_executor( dbs_reader: DbsReader, self_keypair: Ed25519KeyPair, software_version: &'static str, + txs_mempool: duniter_mempools::TxsMempool, ) { BCA_EXECUTOR .set(BcaExecutor { @@ -62,6 +63,7 @@ pub fn set_bca_executor( dbs_reader, self_keypair, software_version, + txs_mempool, }) .unwrap_or_else(|_| panic!("BCA_EXECUTOR already set !")) } @@ -87,6 +89,7 @@ struct BcaExecutor { dbs_reader: DbsReader, self_keypair: Ed25519KeyPair, software_version: &'static str, + txs_mempool: duniter_mempools::TxsMempool, } use uninit::extension_traits::VecCapacity; impl BcaExecutor { @@ -265,6 +268,7 @@ mod tests { Seed32::random().expect("fail to gen random seed"), ), software_version: "test", + txs_mempool: duniter_mempools::TxsMempool::new(10), }) } diff --git a/rust-libs/modules/gva/bca/types/Cargo.toml b/rust-libs/modules/gva/bca/types/Cargo.toml index 69634c022e0f1ced5d652e81e8b78dc1bb833fc0..18bbf84e910a454a2ca9f16cd6344ca4fa1e01c0 100644 --- a/rust-libs/modules/gva/bca/types/Cargo.toml +++ b/rust-libs/modules/gva/bca/types/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" bincode = "1.3" dubp = { version = "0.49.0" } serde = { version = "1.0.105", features = ["derive"] } +smallvec = { version = "1.4.0", features = ["serde"] } thiserror = "1.0.20" [features] diff --git a/rust-libs/modules/gva/bca/types/src/lib.rs b/rust-libs/modules/gva/bca/types/src/lib.rs index 7e71520856449e195019e4e6788cb656642f1c99..0f3a78a339edd6d9bd5d20ed9bd7e9ce517aa846 100644 --- a/rust-libs/modules/gva/bca/types/src/lib.rs +++ b/rust-libs/modules/gva/bca/types/src/lib.rs @@ -23,6 +23,7 @@ )] pub mod prepare_payment; +pub mod rejected_tx; use crate::prepare_payment::{PrepareSimplePayment, PrepareSimplePaymentResp}; @@ -31,6 +32,7 @@ use dubp::crypto::hashs::Hash; use dubp::crypto::keys::ed25519::{PublicKey, Signature}; use dubp::wallet::prelude::*; use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; use thiserror::Error; pub fn bincode_opts() -> impl bincode::Options { @@ -39,26 +41,36 @@ pub fn bincode_opts() -> impl bincode::Options { .allow_trailing_bytes() } -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)] +// Request + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub enum BcaReq { V0(BcaReqV0), _V1, } -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct BcaReqV0 { pub req_id: usize, pub req_type: BcaReqTypeV0, } -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)] +#[allow(clippy::large_enum_variant)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub enum BcaReqTypeV0 { MembersCount, PrepareSimplePayment(PrepareSimplePayment), ProofServerPubkey { challenge: [u8; 16] }, Ping, + SendTxs(Txs), } +// Request types helpers + +pub type Txs = SmallVec<[dubp::documents::transaction::TransactionDocumentV10; 1]>; + +// Response + #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub enum BcaResp { V0(BcaRespV0), @@ -82,8 +94,11 @@ pub enum BcaRespTypeV0 { MembersCount(u64), PrepareSimplePayment(PrepareSimplePaymentResp), Pong, + RejectedTxs(Vec<rejected_tx::RejectedTx>), } +// Result and error + pub type BcaResult = Result<BcaResp, BcaReqExecError>; #[derive(Clone, Debug, Deserialize, Error, PartialEq, Eq, Serialize)] diff --git a/rust-libs/modules/gva/bca/types/src/rejected_tx.rs b/rust-libs/modules/gva/bca/types/src/rejected_tx.rs new file mode 100644 index 0000000000000000000000000000000000000000..14b06e52b76e39ed5830541a422329093151535c --- /dev/null +++ b/rust-libs/modules/gva/bca/types/src/rejected_tx.rs @@ -0,0 +1,30 @@ +// 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, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub struct RejectedTx { + pub tx_index: u16, + pub reason: RejectedTxReason, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub enum RejectedTxReason { + DbError(String), + InvalidTx(String), + MempoolFull, + TxAlreadyWritten, +} diff --git a/rust-libs/modules/gva/src/lib.rs b/rust-libs/modules/gva/src/lib.rs index fdda6a321618c403e54765396d8ca33674ea71c4..277783e8f00b2dcb4955f559d26c752ebf9e4de1 100644 --- a/rust-libs/modules/gva/src/lib.rs +++ b/rust-libs/modules/gva/src/lib.rs @@ -245,6 +245,7 @@ impl GvaModule { duniter_gva_dbs_reader::create_dbs_reader(gva_db_ro), self_keypair, software_version, + mempools.txs, ); let gva_schema = duniter_gva_gql::build_schema_with_data( duniter_gva_gql::GvaSchemaData {