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

chore: import gva module code

parent 821c7556
Branches
No related tags found
No related merge requests found
Showing
with 4362 additions and 0 deletions
# Contributing
For any addition of feature or modification of existing feature, please discuss it beforehand via an issue of this repository by tagging one or more maintainers.
## Commit Message Guidelines
We have very precise rules over how our git commit messages can be formatted. This leads to **more
readable messages** that are easy to follow when looking through the **project history**.
### Commit Message Format
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
```txt
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
The **header** is mandatory and the **scope** of the header is optional.
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.
The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
```txt
docs(changelog): update changelog to beta.5
```
```txt
fix(release): need to depend on latest rxjs and zone.js
The version in our package.json gets copied to the one we publish, and users need the latest of these.
```
### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
### Type
Must be one of the following:
* **build**: Changes that affect the build system or external dependencies (example scopes: crypto, wot)
* **chore**: Modification of the repository architecture
* **ci**: Changes to our CI configuration files and scripts (example scopes: Github Actions, Gitlab CI)
* **docs**: Documentation only changes
* **feat**: Add a new feature
* **mod**: Modify an existing feature
* **fix**: A bug fix
* **perf**: A code change that improves performance
* **refactor**: A code change that neither fixes a bug nor adds a feature nor modify an existing feature
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
* **test**: Adding missing tests or correcting existing tests
### Subject
The subject contains a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter
* no dot (.) at the end
### Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.
### Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference issues that this commit **Closes**.
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
Cargo.lock 0 → 100644
This diff is collapsed.
[package]
name = "duniter-gva"
version = "0.1.0"
authors = ["librelois <elois@duniter.org>"]
license = "AGPL-3.0"
edition = "2018"
[dependencies]
anyhow = "1.0.33"
arrayvec = "0.5.1"
async-graphql = { version = "2.8", features = ["log"] }
async-mutex = "1.4.0"
async-trait = "0.1.41"
bytes = "1.0"
dubp = { version = "0.51.0", features = ["duniter"] }
duniter-bca = { path = "./bca" }
duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core" }
duniter-gva-db = { path = "./db" }
duniter-gva-dbs-reader = { path = "./dbs-reader" }
duniter-gva-indexer = { path = "./indexer" }
duniter-gva-gql = { path = "./gql" }
fast-threadpool = "0.2.3"
flume = "0.10.0"
futures = "0.3.6"
http = "0.2.1"
log = "0.4.11"
resiter = "0.4.0"
serde = { version = "1.0.105", features = ["derive"] }
serde_urlencoded = "0.7.0"
tokio = { version = "1.2", features = ["io-util", "rt-multi-thread"] }
warp = "0.3"
[dev-dependencies]
duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core", features = ["mem"] }
mockall = "0.9.1"
serde_json = "1.0.53"
tokio = { version = "1.2", features = ["macros", "rt-multi-thread", "time"] }
unwrap = "1.2.1"
[workspace]
members = [
"bca",
"db",
"dbs-reader",
"gql",
"indexer",
]
# Duniter GVA
This repository contains the code of the GVA module.
## Duniter repositories
Duniter's code is separated into several git repositories:
* **[dubp-rs-libs]** contains the logic common to Duniter and its customers.
* **[duniter-core]** contains the core code of Duniter.
* The gitlab subgroup **[nodes/rust/modules]** contains the main Duniter modules code (gva, admin, etc).
* The **[duniter]** subgroup contains the "official" implementations of the "duniter-cli" and "duniter-desktop" programs with their default modules (also contains the historical implementation being migrated).
[DuniterModule]: https://git.duniter.org/nodes/rust/duniter-core/blob/main/module/src/lib.rs#L41
[duniter gitlab]: https://git.duniter.org
[dubp-rs-libs]: https://git.duniter.org/libs/dubp-rs-libs
[duniter-core]: https://git.duniter.org/nodes/rust/duniter-core
[duniter]: https://git.duniter.org/nodes/typescript/duniter
[nodes/rust/modules]: https://git.duniter.org/nodes/rust/modules
[package]
name = "duniter-bca"
version = "0.1.0"
authors = ["librelois <elois@duniter.org>"]
license = "AGPL-3.0"
edition = "2018"
[dependencies]
anyhow = "1.0.33"
arrayvec = { version = "0.5.1", features = ["serde"] }
async-bincode = "0.6.1"
async_io_stream = { version = "0.3.1", features = [ "tokio_io"] }
bincode = "1.3"
dubp = { version = "0.51.0", features = ["duniter"] }
duniter-bca-types = { path = "types", features = ["duniter"] }
duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core" }
duniter-gva-db = { path = "../db" }
duniter-gva-dbs-reader = { path = "../dbs-reader" }
fast-threadpool = "0.2.3"
futures = "0.3.6"
once_cell = "1.5"
smallvec = { version = "1.4.0", features = ["serde", "write"] }
tokio = { version = "1.2", features = ["macros", "rt-multi-thread"] }
uninit = "0.4.0"
[dev-dependencies]
duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core", features = ["mem", "mock"] }
duniter-gva-dbs-reader = { path = "../dbs-reader", features = ["mock"] }
tokio = { version = "1.2", features = ["macros", "rt-multi-thread", "time"] }
mockall = "0.9.1"
// 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/>.
mod balances;
mod current_ud;
mod last_blockstamp_out_of_fork_window;
mod members_count;
mod prepare_simple_payment;
mod send_txs;
mod utxos;
use dubp::crypto::keys::KeyPair;
use crate::*;
#[derive(Debug, PartialEq)]
pub(super) struct ExecReqTypeError(pub(super) String);
impl<E> From<E> for ExecReqTypeError
where
E: ToString,
{
fn from(e: E) -> Self {
Self(e.to_string())
}
}
pub(super) async fn execute_req_type(
bca_executor: &BcaExecutor,
req_type: BcaReqTypeV0,
_is_whitelisted: bool,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
match req_type {
BcaReqTypeV0::BalancesOfPubkeys(pubkeys) => {
balances::exec_req_balances_of_pubkeys(bca_executor, pubkeys).await
}
BcaReqTypeV0::FirstUtxosOfPubkeys {
amount_target_opt,
pubkeys,
} => utxos::exec_req_first_utxos_of_pubkeys(bca_executor, amount_target_opt, pubkeys).await,
BcaReqTypeV0::LastBlockstampOutOfForkWindow => {
last_blockstamp_out_of_fork_window::exec_req_last_blockstamp_out_of_fork_window(
bca_executor,
)
.await
}
BcaReqTypeV0::MembersCount => members_count::exec_req_members_count(bca_executor).await,
BcaReqTypeV0::PrepareSimplePayment(params) => {
prepare_simple_payment::exec_req_prepare_simple_payment(bca_executor, params).await
}
BcaReqTypeV0::ProofServerPubkey { challenge } => Ok(BcaRespTypeV0::ProofServerPubkey {
challenge,
server_pubkey: bca_executor.self_keypair.public_key(),
sig: bca_executor
.self_keypair
.generate_signator()
.sign(&challenge),
}),
BcaReqTypeV0::Ping => Ok(BcaRespTypeV0::Pong),
BcaReqTypeV0::SendTxs(txs) => send_txs::send_txs(bca_executor, txs).await,
BcaReqTypeV0::Identities(pubkeys) => {
let dbs_reader = bca_executor.dbs_reader();
Ok(BcaRespTypeV0::Identities(
bca_executor
.dbs_pool
.execute(move |dbs| {
pubkeys
.into_iter()
.map(|pubkey| {
dbs_reader.idty(&dbs.bc_db_ro, pubkey).map(|idty_opt| {
idty_opt.map(|idty| Identity {
is_member: idty.is_member,
username: idty.username,
})
})
})
.collect::<KvResult<ArrayVec<_>>>()
})
.await??,
))
}
BcaReqTypeV0::CurrentUd => current_ud::exec_req_current_ud(bca_executor).await,
BcaReqTypeV0::BalancesOfScripts(scripts) => {
balances::exec_req_balances_of_scripts(bca_executor, scripts).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 dubp::{crypto::keys::ed25519::PublicKey, wallet::prelude::WalletScriptV10};
pub(super) async fn exec_req_balances_of_pubkeys(
bca_executor: &BcaExecutor,
pubkeys: ArrayVec<[PublicKey; 16]>,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
let dbs_reader = bca_executor.dbs_reader();
Ok(BcaRespTypeV0::Balances(
bca_executor
.dbs_pool
.execute(move |_| {
pubkeys
.into_iter()
.map(|pubkey| {
dbs_reader
.get_account_balance(&WalletScriptV10::single_sig(pubkey))
.map(|balance_opt| balance_opt.map(|balance| balance.0))
})
.collect::<Result<ArrayVec<_>, _>>()
})
.await??,
))
}
pub(super) async fn exec_req_balances_of_scripts(
bca_executor: &BcaExecutor,
scripts: ArrayVec<[WalletScriptV10; 16]>,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
let dbs_reader = bca_executor.dbs_reader();
Ok(BcaRespTypeV0::Balances(
bca_executor
.dbs_pool
.execute(move |_| {
scripts
.into_iter()
.map(|script| {
dbs_reader
.get_account_balance(&script)
.map(|balance_opt| balance_opt.map(|balance| balance.0))
})
.collect::<Result<ArrayVec<_>, _>>()
})
.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::*;
pub(super) async fn exec_req_current_ud(
bca_executor: &BcaExecutor,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
if let Some(current_ud) = bca_executor
.cm_accessor
.get_current_meta(|cm| cm.current_ud)
.await
{
Ok(BcaRespTypeV0::CurrentUd(current_ud))
} else {
Err("no blockchain".into())
}
}
// 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::common::prelude::*;
pub(super) async fn exec_req_last_blockstamp_out_of_fork_window(
bca_executor: &BcaExecutor,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
if let Some(current_block_number) = bca_executor
.cm_accessor
.get_current_meta(|cm| cm.current_block_meta.number)
.await
{
let dbs_reader = bca_executor.dbs_reader();
bca_executor
.dbs_pool
.execute(move |dbs| {
let block_ref_number = if current_block_number < 101 {
0
} else {
current_block_number - 101
};
let block_ref_hash = dbs_reader
.block(&dbs.bc_db_ro, U32BE(block_ref_number))?
.expect("unreachable")
.hash;
Ok::<_, ExecReqTypeError>(BcaRespTypeV0::LastBlockstampOutOfForkWindow(
Blockstamp {
number: BlockNumber(block_ref_number),
hash: BlockHash(block_ref_hash),
},
))
})
.await?
} else {
Err("no blockchain".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::*;
#[tokio::test]
async fn test_exec_req_last_blockstamp_out_of_fork_window_no_blockchain() {
let mut cm_mock = MockAsyncAccessor::new();
cm_mock
.expect_get_current_meta::<u32>()
.times(1)
.returning(|_| None);
let dbs_reader = MockDbsReader::new();
let bca_executor =
create_bca_executor(cm_mock, dbs_reader).expect("fail to create bca executor");
let resp_res = exec_req_last_blockstamp_out_of_fork_window(&bca_executor).await;
assert_eq!(resp_res, Err(ExecReqTypeError("no blockchain".into())));
}
#[tokio::test]
async fn test_exec_req_last_blockstamp_out_of_fork_window_ok() -> Result<(), ExecReqTypeError> {
let mut cm_mock = MockAsyncAccessor::new();
cm_mock
.expect_get_current_meta::<u32>()
.times(1)
.returning(|f| Some(f(&CurrentMeta::default())));
let mut dbs_reader = MockDbsReader::new();
dbs_reader
.expect_block()
.times(1)
.returning(|_, _| Ok(Some(BlockMetaV2::default())));
let bca_executor =
create_bca_executor(cm_mock, dbs_reader).expect("fail to create bca executor");
let resp = exec_req_last_blockstamp_out_of_fork_window(&bca_executor).await?;
assert_eq!(
resp,
BcaRespTypeV0::LastBlockstampOutOfForkWindow(Blockstamp::default())
);
Ok(())
}
}
// 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(super) async fn exec_req_members_count(
bca_executor: &BcaExecutor,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
if let Some(members_count) = bca_executor
.cm_accessor
.get_current_meta(|cm| cm.current_block_meta.members_count)
.await
{
Ok(BcaRespTypeV0::MembersCount(members_count))
} else {
Err("no blockchain".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::*;
#[tokio::test]
async fn test_exec_req_members_count() {
let mut cm_mock = MockAsyncAccessor::new();
cm_mock
.expect_get_current_meta::<u64>()
.times(1)
.returning(|f| Some(f(&CurrentMeta::default())));
let dbs_reader = MockDbsReader::new();
let bca_executor =
create_bca_executor(cm_mock, dbs_reader).expect("fail to create bca executor");
let resp_res = exec_req_members_count(&bca_executor).await;
assert_eq!(resp_res, Ok(BcaRespTypeV0::MembersCount(0)));
}
}
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software current_block_number: (), current_block_hash: (), inputs: (), inputs_sum: (): 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::wallet::prelude::*;
use duniter_bca_types::prepare_payment::{PrepareSimplePayment, PrepareSimplePaymentResp};
pub(super) async fn exec_req_prepare_simple_payment(
bca_executor: &BcaExecutor,
params: PrepareSimplePayment,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
let issuer = params.issuer;
if let Some(current_meta) = bca_executor.cm_accessor.get_current_meta(|cm| *cm).await {
let current_block_meta = current_meta.current_block_meta;
let current_ud = current_meta.current_ud;
let dbs_reader = bca_executor.dbs_reader();
let (amount, block_ref_number, block_ref_hash, (inputs, inputs_sum)) = bca_executor
.dbs_pool
.execute(move |dbs| {
let mut amount = params.amount.to_cents(current_ud);
let block_ref_number = if current_block_meta.number < 101 {
0
} else {
current_block_meta.number - 101
};
let block_ref_hash = dbs_reader
.block(&dbs.bc_db_ro, U32BE(block_ref_number))?
.expect("unreachable")
.hash;
let current_base = current_block_meta.unit_base as i64;
if amount.base() > current_base {
Err("too long base".into())
} else {
while amount.base() < current_base {
amount = amount.increment_base();
}
Ok::<_, ExecReqTypeError>((
amount,
block_ref_number,
block_ref_hash,
dbs_reader.find_inputs(
&dbs.bc_db_ro,
&dbs.txs_mp_db,
amount,
&WalletScriptV10::single(WalletConditionV10::Sig(issuer)),
false,
)?,
))
}
})
.await??;
if inputs_sum < amount {
return Err("insufficient balance".into());
}
Ok(BcaRespTypeV0::PrepareSimplePayment(
PrepareSimplePaymentResp {
current_block_number: block_ref_number,
current_block_hash: block_ref_hash,
current_ud,
inputs,
inputs_sum,
},
))
} else {
Err("no blockchain".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::*;
#[tokio::test]
async fn test_exec_req_prepare_simple_payment_no_blockchain() {
let mut mock_cm = MockAsyncAccessor::new();
mock_cm
.expect_get_current_meta::<CurrentMeta>()
.times(1)
.returning(|_| None);
let dbs_reader = MockDbsReader::new();
let bca_executor =
create_bca_executor(mock_cm, dbs_reader).expect("fail to create bca executor");
let resp_res = exec_req_prepare_simple_payment(
&bca_executor,
PrepareSimplePayment {
issuer: PublicKey::default(),
amount: Amount::Cents(SourceAmount::new(42, 0)),
},
)
.await;
assert_eq!(resp_res, Err(ExecReqTypeError("no blockchain".into())));
}
#[tokio::test]
async fn test_exec_req_prepare_simple_payment_too_long_base() {
let mut mock_cm = MockAsyncAccessor::new();
mock_cm
.expect_get_current_meta::<CurrentMeta>()
.times(1)
.returning(|f| Some(f(&CurrentMeta::default())));
let mut dbs_reader = MockDbsReader::new();
dbs_reader
.expect_block()
.times(1)
.returning(|_, _| Ok(Some(BlockMetaV2::default())));
let bca_executor =
create_bca_executor(mock_cm, dbs_reader).expect("fail to create bca executor");
let resp_res = exec_req_prepare_simple_payment(
&bca_executor,
PrepareSimplePayment {
issuer: PublicKey::default(),
amount: Amount::Cents(SourceAmount::new(42, 1)),
},
)
.await;
assert_eq!(resp_res, Err(ExecReqTypeError("too long base".into())));
}
#[tokio::test]
async fn test_exec_req_prepare_simple_payment_insufficient_balance() {
let mut mock_cm = MockAsyncAccessor::new();
mock_cm
.expect_get_current_meta::<CurrentMeta>()
.times(1)
.returning(|f| Some(f(&CurrentMeta::default())));
let mut dbs_reader = MockDbsReader::new();
dbs_reader
.expect_block()
.times(1)
.returning(|_, _| Ok(Some(BlockMetaV2::default())));
dbs_reader
.expect_find_inputs::<TxsMpV2Db<FileBackend>>()
.times(1)
.returning(|_, _, _, _, _| Ok((vec![], SourceAmount::default())));
let bca_executor =
create_bca_executor(mock_cm, dbs_reader).expect("fail to create bca executor");
let resp_res = exec_req_prepare_simple_payment(
&bca_executor,
PrepareSimplePayment {
issuer: PublicKey::default(),
amount: Amount::Cents(SourceAmount::new(42, 0)),
},
)
.await;
assert_eq!(
resp_res,
Err(ExecReqTypeError("insufficient balance".into()))
);
}
#[tokio::test]
async fn test_exec_req_prepare_simple_payment_ok() -> Result<(), ExecReqTypeError> {
let input = TransactionInputV10 {
amount: SourceAmount::with_base0(57),
id: SourceIdV10::Utxo(UtxoIdV10 {
tx_hash: Hash::default(),
output_index: 3,
}),
};
let mut mock_cm = MockAsyncAccessor::new();
mock_cm
.expect_get_current_meta::<CurrentMeta>()
.times(1)
.returning(|f| Some(f(&CurrentMeta::default())));
let mut dbs_reader = MockDbsReader::new();
dbs_reader
.expect_block()
.times(1)
.returning(|_, _| Ok(Some(BlockMetaV2::default())));
dbs_reader
.expect_find_inputs::<TxsMpV2Db<FileBackend>>()
.times(1)
.returning(move |_, _, _, _, _| Ok((vec![input], SourceAmount::with_base0(57))));
let bca_executor =
create_bca_executor(mock_cm, dbs_reader).expect("fail to create bca executor");
let resp = exec_req_prepare_simple_payment(
&bca_executor,
PrepareSimplePayment {
issuer: PublicKey::default(),
amount: Amount::Cents(SourceAmount::new(42, 0)),
},
)
.await?;
assert_eq!(
resp,
BcaRespTypeV0::PrepareSimplePayment(PrepareSimplePaymentResp {
current_block_number: 0,
current_block_hash: Hash::default(),
current_ud: SourceAmount::ZERO,
inputs: vec![input],
inputs_sum: SourceAmount::with_base0(57),
})
);
Ok(())
}
}
// 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_core::mempools::TxMpError::Db(e) => {
RejectedTxReason::DbError(e.to_string())
}
duniter_core::mempools::TxMpError::Full => {
RejectedTxReason::MempoolFull
}
duniter_core::mempools::TxMpError::TxAlreadyWritten => {
RejectedTxReason::TxAlreadyWritten
}
},
})
})
.await?
{
rejected_txs.push(rejected_tx);
}
}
Ok(BcaRespTypeV0::RejectedTxs(rejected_txs))
}
// 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::ed25519::PublicKey, wallet::prelude::WalletScriptV10};
pub(super) async fn exec_req_first_utxos_of_pubkeys(
bca_executor: &BcaExecutor,
amount_target_opt: Option<Amount>,
pubkeys: ArrayVec<[PublicKey; 16]>,
) -> Result<BcaRespTypeV0, ExecReqTypeError> {
if let Some(current_ud) = bca_executor
.cm_accessor
.get_current_meta(|cm| cm.current_ud)
.await
{
let dbs_reader = bca_executor.dbs_reader();
let scripts: ArrayVec<[WalletScriptV10; 16]> = pubkeys
.into_iter()
.map(WalletScriptV10::single_sig)
.collect();
if let Some(amount_target) = amount_target_opt {
Ok(BcaRespTypeV0::FirstUtxosOfPubkeys(
bca_executor
.dbs_pool
.execute(move |_| {
Ok::<_, ExecReqTypeError>(dbs_reader.first_scripts_utxos(
Some(amount_target.to_cents(current_ud)),
40,
&scripts,
)?)
})
.await??,
))
} else {
Ok(BcaRespTypeV0::FirstUtxosOfPubkeys(
bca_executor
.dbs_pool
.execute(move |_| dbs_reader.first_scripts_utxos(None, 40, &scripts))
.await??,
))
}
} else {
Err("no blockchain".into())
}
}
// Copyright (C) 2020 Éloïs req_id: (), resp_type: ()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/>.
#![deny(
clippy::unwrap_used,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unstable_features,
unused_import_braces
)]
mod exec_req_type;
const MAX_BATCH_SIZE: usize = 10;
const RESP_MIN_SIZE: usize = 64;
type RespBytes = SmallVec<[u8; RESP_MIN_SIZE]>;
use crate::exec_req_type::ExecReqTypeError;
#[cfg(test)]
use crate::tests::AsyncAccessor;
use arrayvec::ArrayVec;
use async_bincode::AsyncBincodeReader;
use async_io_stream::IoStream;
use bincode::Options as _;
use dubp::crypto::keys::{ed25519::Ed25519KeyPair, Signator};
use duniter_bca_types::{
amount::Amount, bincode_opts, identity::Identity, BcaReq, BcaReqExecError, BcaReqTypeV0,
BcaResp, BcaRespTypeV0, BcaRespV0,
};
pub use duniter_core::dbs::kv_typed::prelude::*;
use duniter_core::dbs::{FileBackend, SharedDbs};
#[cfg(not(test))]
use duniter_core::global::AsyncAccessor;
use duniter_gva_dbs_reader::DbsReader;
use futures::{prelude::stream::FuturesUnordered, StreamExt, TryStream, TryStreamExt};
use once_cell::sync::OnceCell;
use smallvec::SmallVec;
use tokio::task::JoinError;
#[cfg(test)]
use crate::tests::DbsReaderImpl;
#[cfg(not(test))]
use duniter_gva_dbs_reader::DbsReaderImpl;
static BCA_EXECUTOR: OnceCell<BcaExecutor> = OnceCell::new();
pub fn set_bca_executor(
currency: String,
cm_accessor: AsyncAccessor,
dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<SharedDbs<FileBackend>>,
dbs_reader: DbsReaderImpl,
self_keypair: Ed25519KeyPair,
software_version: &'static str,
txs_mempool: duniter_core::mempools::TxsMempool,
) {
BCA_EXECUTOR
.set(BcaExecutor {
currency,
cm_accessor,
dbs_pool,
dbs_reader,
self_keypair,
software_version,
txs_mempool,
})
.unwrap_or_else(|_| panic!("BCA_EXECUTOR already set !"))
}
#[cfg(not(test))]
pub async fn execute<B, S>(query_body_stream: S, is_whitelisted: bool) -> Vec<u8>
where
B: AsRef<[u8]>,
S: 'static + TryStream<Ok = B, Error = std::io::Error> + Send + Unpin,
{
unsafe {
BCA_EXECUTOR
.get_unchecked()
.execute(query_body_stream, is_whitelisted)
.await
}
}
#[derive(Clone)]
struct BcaExecutor {
cm_accessor: AsyncAccessor,
currency: String,
dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<SharedDbs<FileBackend>>,
dbs_reader: DbsReaderImpl,
self_keypair: Ed25519KeyPair,
software_version: &'static str,
txs_mempool: duniter_core::mempools::TxsMempool,
}
use uninit::extension_traits::VecCapacity;
impl BcaExecutor {
pub async fn execute<B, S>(&self, query_body_stream: S, is_whitelisted: bool) -> Vec<u8>
where
B: AsRef<[u8]>,
S: 'static + TryStream<Ok = B, Error = std::io::Error> + Send + Unpin,
{
let async_bincode_reader =
AsyncBincodeReader::<IoStream<S, B>, BcaReq>::from(IoStream::new(query_body_stream));
self.execute_inner(async_bincode_reader, is_whitelisted)
.await
.into_iter()
.fold(Vec::new(), |mut vec, elem| {
// Write resp len
let out = vec.reserve_uninit(4);
out.copy_from_slice(&u32::to_be_bytes(elem.len() as u32)[..]);
unsafe {
// # Safety
//
// - `.copy_from_slice()` contract guarantees initialization
// of `out`, which, in turn, from `reserve_uninit`'s contract,
// leads to the `vec` extra capacity having been initialized.
vec.set_len(vec.len() + 4);
}
// Write resp content
let out = vec.reserve_uninit(elem.len());
out.copy_from_slice(&elem[..]);
unsafe {
// # Safety
//
// - `.copy_from_slice()` contract guarantees initialization
// of `out`, which, in turn, from `reserve_uninit`'s contract,
// leads to the `vec` extra capacity having been initialized.
vec.set_len(vec.len() + elem.len());
}
vec
})
}
async fn execute_inner(
&self,
stream: impl TryStream<Ok = BcaReq, Error = bincode::Error>,
is_whitelisted: bool,
) -> Vec<RespBytes> {
match stream
.map_ok(|req| {
let self_clone = self.clone();
tokio::spawn(async move { self_clone.execute_req(req, is_whitelisted).await })
})
.take(MAX_BATCH_SIZE)
.try_collect::<FuturesUnordered<_>>()
.await
{
Ok(futures_unordered) => {
futures_unordered
.map(|req_res: Result<BcaResp, JoinError>| {
let resp = match req_res {
Ok(resp) => Ok(resp),
Err(e) => Err(if e.is_cancelled() {
BcaReqExecError::Cancelled
} else if e.is_panic() {
BcaReqExecError::Panic
} else {
BcaReqExecError::Unknown
}),
};
let mut resp_buffer = RespBytes::new();
bincode_opts()
.serialize_into(&mut resp_buffer, &resp)
.expect("unreachable");
resp_buffer
})
.collect()
.await
}
Err(e) => {
let req_res: Result<BcaResp, BcaReqExecError> =
Err(BcaReqExecError::InvalidReq(e.to_string()));
let mut resp_buffer = RespBytes::new();
bincode_opts()
.serialize_into(&mut resp_buffer, &req_res)
.expect("unreachable");
vec![resp_buffer]
}
}
}
#[inline(always)]
async fn execute_req(self, req: BcaReq, is_whitelisted: bool) -> BcaResp {
match req {
BcaReq::V0(req) => BcaResp::V0(BcaRespV0 {
req_id: req.req_id,
resp_type: match crate::exec_req_type::execute_req_type(
&self,
req.req_type,
is_whitelisted,
)
.await
{
Ok(resp_type) => resp_type,
Err(e) => BcaRespTypeV0::Error(e.0),
},
}),
_ => BcaResp::UnsupportedVersion,
}
}
}
#[cfg(not(test))]
impl BcaExecutor {
#[inline(always)]
pub fn dbs_reader(&self) -> DbsReaderImpl {
self.dbs_reader
}
}
#[cfg(test)]
mod tests {
use super::*;
pub use dubp::{
block::prelude::*,
crypto::{
hashs::Hash,
keys::{ed25519::PublicKey, KeyPair, Seed32},
},
documents::transaction::TransactionInputV10,
wallet::prelude::*,
};
pub use duniter_bca_types::BcaReqV0;
pub use duniter_core::dbs::databases::bc_v2::{BcV2DbReadable, BcV2DbRo};
pub use duniter_core::dbs::databases::cm_v1::{CmV1Db, CmV1DbReadable};
pub use duniter_core::dbs::databases::txs_mp_v2::{TxsMpV2Db, TxsMpV2DbReadable};
pub use duniter_core::dbs::BlockMetaV2;
pub use duniter_core::global::{CurrentMeta, MockAsyncAccessor};
pub use duniter_gva_dbs_reader::MockDbsReader;
pub use futures::TryStreamExt;
pub type AsyncAccessor = duniter_core::dbs::kv_typed::prelude::Arc<MockAsyncAccessor>;
pub type DbsReaderImpl = duniter_core::dbs::kv_typed::prelude::Arc<MockDbsReader>;
impl BcaExecutor {
#[inline(always)]
pub fn dbs_reader(&self) -> DbsReaderImpl {
self.dbs_reader.clone()
}
}
pub(crate) fn create_bca_executor(
mock_cm: MockAsyncAccessor,
mock_dbs_reader: MockDbsReader,
) -> KvResult<BcaExecutor> {
let dbs = SharedDbs::mem()?;
let threadpool =
fast_threadpool::ThreadPool::start(fast_threadpool::ThreadPoolConfig::low(), dbs);
Ok(BcaExecutor {
cm_accessor: duniter_core::dbs::kv_typed::prelude::Arc::new(mock_cm),
currency: "g1".to_owned(),
dbs_pool: threadpool.into_async_handler(),
dbs_reader: duniter_core::dbs::kv_typed::prelude::Arc::new(mock_dbs_reader),
self_keypair: Ed25519KeyPair::from_seed(
Seed32::random().expect("fail to gen random seed"),
),
software_version: "test",
txs_mempool: duniter_core::mempools::TxsMempool::new(10),
})
}
pub(crate) fn io_stream<B: AsRef<[u8]>>(
bytes: B,
) -> impl TryStream<Ok = B, Error = std::io::Error> {
futures::stream::iter(std::iter::once(Ok(bytes)))
}
#[tokio::test]
async fn test_one_req_ok() -> Result<(), bincode::Error> {
let req = BcaReq::V0(BcaReqV0 {
req_id: 42,
req_type: BcaReqTypeV0::MembersCount,
});
assert_eq!(bincode_opts().serialized_size(&req)?, 3);
let mut bytes = [0u8; 7];
bincode_opts().serialize_into(&mut bytes[4..], &req)?;
bytes[3] = 3;
use bincode::Options;
//println!("bytes_for_bincode={:?}", &bytes[4..]);
assert_eq!(req, bincode_opts().deserialize(&bytes[4..])?);
let mut mock_cm = MockAsyncAccessor::new();
mock_cm
.expect_get_current_meta::<u64>()
.times(1)
.returning(|f| Some(f(&CurrentMeta::default())));
let bca_executor = create_bca_executor(mock_cm, MockDbsReader::new())
.expect("fail to create bca executor");
//println!("bytes={:?}", bytes);
let bytes_res = bca_executor.execute(io_stream(bytes), false).await;
//println!("bytes_res={:?}", bytes_res);
let bca_res: Vec<Result<BcaResp, BcaReqExecError>> =
AsyncBincodeReader::<_, Result<BcaResp, BcaReqExecError>>::from(&bytes_res[..])
.try_collect::<Vec<_>>()
.await?;
assert_eq!(
bca_res,
vec![Ok(BcaResp::V0(BcaRespV0 {
req_id: 42,
resp_type: BcaRespTypeV0::MembersCount(0)
}))]
);
Ok(())
}
#[tokio::test]
async fn test_one_req_invalid() -> Result<(), bincode::Error> {
let req = BcaReq::V0(BcaReqV0 {
req_id: 42,
req_type: BcaReqTypeV0::MembersCount,
});
assert_eq!(bincode_opts().serialized_size(&req)?, 3);
let mut bytes = [0u8; 7];
bincode_opts().serialize_into(&mut bytes[4..], &req)?;
bytes[3] = 2;
use bincode::Options;
//println!("bytes_for_bincode={:?}", &bytes[4..]);
assert_eq!(req, bincode_opts().deserialize(&bytes[4..])?);
let bca_executor = create_bca_executor(MockAsyncAccessor::new(), MockDbsReader::new())
.expect("fail to create bca executor");
//println!("bytes={:?}", bytes);
let bytes_res = bca_executor.execute(io_stream(bytes), false).await;
//println!("bytes_res={:?}", bytes_res);
let bca_res: Vec<Result<BcaResp, BcaReqExecError>> =
AsyncBincodeReader::<_, Result<BcaResp, BcaReqExecError>>::from(&bytes_res[..])
.try_collect::<Vec<_>>()
.await?;
assert_eq!(
bca_res,
vec![Err(BcaReqExecError::InvalidReq(
"io error: unexpected end of file".to_owned()
))]
);
Ok(())
}
#[tokio::test]
async fn test_two_reqs_ok() -> Result<(), bincode::Error> {
let req1 = BcaReq::V0(BcaReqV0 {
req_id: 42,
req_type: BcaReqTypeV0::Ping,
});
assert_eq!(bincode_opts().serialized_size(&req1)?, 3);
let req2 = BcaReq::V0(BcaReqV0 {
req_id: 57,
req_type: BcaReqTypeV0::MembersCount,
});
assert_eq!(bincode_opts().serialized_size(&req2)?, 3);
let mut bytes = [0u8; 14];
bincode_opts().serialize_into(&mut bytes[4..], &req1)?;
bytes[3] = 3;
bincode_opts().serialize_into(&mut bytes[11..], &req2)?;
bytes[10] = 3;
let mut mock_cm = MockAsyncAccessor::new();
mock_cm
.expect_get_current_meta::<u64>()
.times(1)
.returning(|f| Some(f(&CurrentMeta::default())));
let bca_executor = create_bca_executor(mock_cm, MockDbsReader::new())
.expect("fail to create bca executor");
//println!("bytes={:?}", bytes);
let bytes_res = bca_executor.execute(io_stream(bytes), false).await;
//println!("bytes_res={:?}", bytes_res);
let bca_res: Vec<Result<BcaResp, BcaReqExecError>> =
AsyncBincodeReader::<_, Result<BcaResp, BcaReqExecError>>::from(&bytes_res[..])
.try_collect::<Vec<_>>()
.await?;
assert_eq!(
bca_res,
vec![
Ok(BcaResp::V0(BcaRespV0 {
req_id: 42,
resp_type: BcaRespTypeV0::Pong
})),
Ok(BcaResp::V0(BcaRespV0 {
req_id: 57,
resp_type: BcaRespTypeV0::MembersCount(0)
}))
]
);
Ok(())
}
}
[package]
name = "duniter-bca-types"
version = "0.1.0"
authors = ["librelois <elois@duniter.org>"]
license = "AGPL-3.0"
edition = "2018"
[dependencies]
arrayvec = { version = "0.5.1", features = ["serde"] }
bincode = "1.3"
dubp = { version = "0.51.0" }
serde = { version = "1.0.105", features = ["derive"] }
smallvec = { version = "1.4.0", features = ["serde"] }
thiserror = "1.0.20"
[features]
default = ["duniter"]
client = ["dubp/client"]
duniter = ["dubp/duniter"]
// 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, Debug, Deserialize, PartialEq, Serialize)]
pub enum Amount {
Cents(SourceAmount),
Uds(f64),
}
impl Default for Amount {
fn default() -> Self {
Self::Cents(SourceAmount::ZERO)
}
}
impl Amount {
pub fn to_cents(self, ud_amount: SourceAmount) -> SourceAmount {
match self {
Amount::Cents(sa) => sa,
Amount::Uds(f64_) => {
if !f64_.is_finite() || f64_ <= 0f64 {
SourceAmount::ZERO
} else {
SourceAmount::new(
f64::round(ud_amount.amount() as f64 * f64_) as i64,
ud_amount.base(),
)
}
}
}
}
}
// 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, Eq, PartialEq, Serialize)]
pub struct Identity {
pub is_member: bool,
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/>.
#![deny(
clippy::unwrap_used,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unstable_features,
unused_import_braces
)]
pub mod amount;
pub mod identity;
pub mod prepare_payment;
pub mod rejected_tx;
pub mod utxo;
use crate::amount::Amount;
use crate::identity::Identity;
use crate::prepare_payment::{PrepareSimplePayment, PrepareSimplePaymentResp};
use crate::utxo::Utxo;
use arrayvec::ArrayVec;
use bincode::Options as _;
use dubp::crypto::keys::ed25519::{PublicKey, Signature};
use dubp::wallet::prelude::*;
use dubp::{common::prelude::Blockstamp, crypto::hashs::Hash};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use thiserror::Error;
// Constants
pub const MAX_FIRST_UTXOS: usize = 40;
// Request
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum BcaReq {
V0(BcaReqV0),
_V1,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct BcaReqV0 {
pub req_id: usize,
pub req_type: BcaReqTypeV0,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum BcaReqTypeV0 {
BalancesOfPubkeys(ArrayVec<[PublicKey; 16]>),
FirstUtxosOfPubkeys {
amount_target_opt: Option<Amount>,
pubkeys: ArrayVec<[PublicKey; 16]>,
},
LastBlockstampOutOfForkWindow,
MembersCount,
PrepareSimplePayment(PrepareSimplePayment),
ProofServerPubkey {
challenge: [u8; 16],
},
Ping,
SendTxs(Txs),
Identities(ArrayVec<[PublicKey; 16]>),
CurrentUd,
BalancesOfScripts(ArrayVec<[WalletScriptV10; 16]>),
}
// 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),
UnsupportedVersion,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct BcaRespV0 {
pub req_id: usize,
pub resp_type: BcaRespTypeV0,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub enum BcaRespTypeV0 {
Error(String),
Balances(ArrayVec<[Option<SourceAmount>; 16]>),
FirstUtxosOfPubkeys(Vec<ArrayVec<[Utxo; MAX_FIRST_UTXOS]>>),
ProofServerPubkey {
challenge: [u8; 16],
server_pubkey: PublicKey,
sig: Signature,
},
LastBlockstampOutOfForkWindow(Blockstamp),
MembersCount(u64),
PrepareSimplePayment(PrepareSimplePaymentResp),
Pong,
RejectedTxs(Vec<rejected_tx::RejectedTx>),
Identities(ArrayVec<[Option<Identity>; 16]>),
CurrentUd(SourceAmount),
}
// Result and error
pub type BcaResult = Result<BcaResp, BcaReqExecError>;
#[derive(Clone, Debug, Deserialize, Error, PartialEq, Eq, Serialize)]
pub enum BcaReqExecError {
#[error("task cancelled")]
Cancelled,
#[error("Invalid request: {0}")]
InvalidReq(String),
#[error("task panicked")]
Panic,
#[error("Unknown error")]
Unknown,
}
// Bincode configuration
pub fn bincode_opts() -> impl bincode::Options {
bincode::options()
.with_limit(u32::max_value() as u64)
.allow_trailing_bytes()
}
// 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;
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct PrepareSimplePayment {
pub issuer: PublicKey,
pub amount: Amount,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct PrepareSimplePaymentResp {
pub current_block_number: u32,
pub current_block_hash: Hash,
pub current_ud: SourceAmount,
pub inputs: Vec<TransactionInputV10>,
pub inputs_sum: SourceAmount,
}
// 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,
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment