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

fix(gdev): add constant tx fees (2 cents by tx) to prevent #62

Fix #62
parent 38690cfe
No related branches found
No related tags found
No related merge requests found
# Duniter-v2s integration tests
# Duniter-v2s end2end tests
## cucumber functionnal tests
......@@ -47,37 +47,9 @@ Feature: My awesome feature
Then We should observe that
```
### Test users
6 test users are provided:
- alice
- bob
- charlie
- dave
- eve
- ferdie
### genesis state
Each scenario bootstraps its own blockchain with its own genesis state.
By default, all scenarios use the same configuration for the genesis, which is located in the file
`/cucumber-genesis/default.json`.
### Steps
You can define a custom genesis state for each scenario with the tag `@genesis.confName`.
The genesis configuration must then be defined in a json file located at
`/cucumber-genesis/confName.json`.
You can also define a custom genesis at the feature level, all the scenarios of this feature will
then inherit the genesis configuration.
### Currency amounts
Amounts must be expressed as an integer of `ĞD` or `UD`, decimal numbers are not supported.
If you need more precision, you can express amounts in cents of ĞD (write `cĞD`), or in thousandths
of UD (write `mUD`).
Each scenario is a list of steps. In our context (blockchain), only the `When` and `Then` steps make sense, `Given` being the genesis.
#### When
......@@ -95,23 +67,54 @@ List of possible actions:
Example: `alice should have 10 ĞD`
### Universal dividend creation
#### Then
- Check the current UD amount
Usage: `Current UD amount should be {amount}.{cents} ĞD`
Example: `Current UD amount should be 10.00 ĞD`
- Check the monetary mass
Usage: `Monetary mass should be {amount}.{cents} ĞD`
Example: `Monetary mass should be 30.00 ĞD`
### Test users
6 test users are provided:
- alice
- bob
- charlie
- dave
- eve
- ferdie
### Currency amounts
Amounts must be expressed as an integer of `ĞD` or `UD`, decimal numbers are not supported.
If you need more precision, you can express amounts in cents of ĞD (write `cĞD`), or in thousandths
of UD (write `mUD`).
### genesis state
Each scenario bootstraps its own blockchain with its own genesis state.
By default, all scenarios use the same configuration for the genesis, which is located in the file
`/cucumber-genesis/default.json`.
You can define a custom genesis state for each scenario with the tag `@genesis.confName`.
The genesis configuration must then be defined in a json file located at
`/cucumber-genesis/confName.json`.
You can also define a custom genesis at the feature level, all the scenarios of this feature will
then inherit the genesis configuration.
### ignoreErrors
For some scenarios, you may need to perform an action (When) that fails voluntarily, in this case you must add the tag @ignoreErrors to your scenario, otherwise it will be considered as failed
### Run cucumber functional tests
To run the cucumber tests, you will need to have the rust toolchain installed locally.
......
......@@ -26,6 +26,7 @@ Feature: Balance transfer
"""
Then eve should have 2 ĞD
@ignoreErrors
Scenario: Create a new account without any founds
Then eve should have 0 ĞD
When eve send 0 ĞD to alice
......
@genesis.default
Feature: Balance transfer all
Scenario: If alice sends all her ĞDs to Dave, Dave will get 8 ĞD
Scenario: If alice sends all her ĞDs to Dave
When alice sends all her ĞDs to dave
"""
Alice is a smith member, as such she is not allowed to empty her account completely,
if she tries to do so, the existence deposit (2 ĞD) must remain.
"""
Then alice should have 2 ĞD
Then dave should have 8 ĞD
"""
10 ĞD (initial Alice balance) - 2 ĞD (Existential deposit) - 0.02 ĞD (transaction fees)
"""
Then dave should have 798 cĞD
......@@ -29,33 +29,44 @@ use std::sync::{
};
#[derive(WorldInit)]
pub struct DuniterWorld(Option<DuniterWorldInner>);
pub struct DuniterWorld {
ignore_errors: bool,
inner: Option<DuniterWorldInner>,
}
impl DuniterWorld {
// Write methods
async fn init(&mut self, maybe_genesis_conf_file: Option<PathBuf>) {
if let Some(ref mut inner) = self.0 {
if let Some(ref mut inner) = self.inner {
inner.kill();
}
self.inner = Some(DuniterWorldInner::new(maybe_genesis_conf_file).await);
}
fn kill(&mut self) {
if let Some(ref mut inner) = self.inner {
inner.kill();
}
self.0 = Some(DuniterWorldInner::new(maybe_genesis_conf_file).await);
}
fn set_ignore_errors(&mut self, ignore_errors: bool) {
self.ignore_errors = ignore_errors;
}
// Read methods
fn api(&self) -> &Api {
if let Some(ref inner) = self.0 {
if let Some(ref inner) = self.inner {
&inner.api
} else {
panic!("uninit")
}
}
fn client(&self) -> &Client {
if let Some(ref inner) = self.0 {
if let Some(ref inner) = self.inner {
&inner.client
} else {
panic!("uninit")
}
}
fn kill(&mut self) {
if let Some(ref mut inner) = self.0 {
inner.kill();
}
fn ignore_errors(&self) -> bool {
self.ignore_errors
}
}
......@@ -71,7 +82,10 @@ impl World for DuniterWorld {
type Error = Infallible;
async fn new() -> std::result::Result<Self, Infallible> {
Ok(DuniterWorld(None))
Ok(Self {
ignore_errors: false,
inner: None,
})
}
}
......@@ -148,10 +162,16 @@ async fn transfer(
let to = AccountKeyring::from_str(&to).expect("unknown to");
let (amount, is_ud) = parse_amount(amount, &unit);
if is_ud {
let res = if is_ud {
common::balances::transfer_ud(world.api(), world.client(), from, amount, to).await
} else {
common::balances::transfer(world.api(), world.client(), from, amount, to).await
};
if world.ignore_errors() {
Ok(())
} else {
res
}
}
......@@ -164,13 +184,18 @@ async fn send_all_to(world: &mut DuniterWorld, from: String, to: String) -> Resu
common::balances::transfer_all(world.api(), world.client(), from, to).await
}
#[then(regex = r"([a-zA-Z]+) should have (\d+) ĞD")]
async fn should_have(world: &mut DuniterWorld, who: String, amount: u64) -> Result<()> {
#[then(regex = r"([a-zA-Z]+) should have (\d+) (ĞD|cĞD)")]
async fn should_have(
world: &mut DuniterWorld,
who: String,
amount: u64,
unit: String,
) -> Result<()> {
// Parse inputs
let who = AccountKeyring::from_str(&who)
.expect("unknown to")
.to_account_id();
let amount = amount * 100;
let (amount, _is_ud) = parse_amount(amount, &unit);
let who_account = world.api().storage().system().account(who, None).await?;
assert_eq!(who_account.data.free, amount);
......@@ -250,6 +275,7 @@ async fn main() {
"{}.json",
genesis_conf_name(&feature.tags, &scenario.tags)
));
world.set_ignore_errors(ignore_errors(&scenario.tags));
Box::pin(world.init(Some(genesis_conf_file_path)))
})
.after(move |_feature, _rule, _scenario, maybe_world| {
......@@ -280,3 +306,12 @@ fn genesis_conf_name(feature_tags: &[String], scenario_tags: &[String]) -> Strin
}
"default".to_owned()
}
fn ignore_errors(scenario_tags: &[String]) -> bool {
for tag in scenario_tags {
if tag == "ignoreErrors" {
return true;
}
}
false
}
......@@ -17,7 +17,7 @@
pub use frame_support::weights::{
Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial,
};
use sp_arithmetic::traits::{BaseArithmetic, Unsigned, Zero};
use sp_arithmetic::traits::{BaseArithmetic, One, Unsigned};
pub struct WeightToFeeImpl<T>(sp_std::marker::PhantomData<T>);
......@@ -35,8 +35,8 @@ where
degree: 1,
})
}
// Force disable fees
// Force constant fees
fn calc(_weight: &Weight) -> Self::Balance {
Zero::zero()
One::one()
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment