From eaf95719982ea4746872c79e65780a6491036aa9 Mon Sep 17 00:00:00 2001 From: librelois <c@elo.tf> Date: Sun, 5 Jun 2022 17:14:49 +0200 Subject: [PATCH] fix(gdev): add constant tx fees (2 cents by tx) to prevent #62 Fix #62 --- end2end-tests/README.md | 75 ++++++++++--------- .../account_creation.feature | 1 + .../cucumber-features/transfer_all.feature | 7 +- end2end-tests/tests/cucumber_tests.rs | 63 ++++++++++++---- runtime/common/src/fees.rs | 6 +- 5 files changed, 97 insertions(+), 55 deletions(-) diff --git a/end2end-tests/README.md b/end2end-tests/README.md index 8dfd383e4..c15fc019d 100644 --- a/end2end-tests/README.md +++ b/end2end-tests/README.md @@ -1,4 +1,4 @@ -# 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. diff --git a/end2end-tests/cucumber-features/account_creation.feature b/end2end-tests/cucumber-features/account_creation.feature index bc2fc4e78..e3df0d149 100644 --- a/end2end-tests/cucumber-features/account_creation.feature +++ b/end2end-tests/cucumber-features/account_creation.feature @@ -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 diff --git a/end2end-tests/cucumber-features/transfer_all.feature b/end2end-tests/cucumber-features/transfer_all.feature index b111bb518..00077ebd6 100644 --- a/end2end-tests/cucumber-features/transfer_all.feature +++ b/end2end-tests/cucumber-features/transfer_all.feature @@ -1,11 +1,14 @@ @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 diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs index ffe54030a..a039bc2b7 100644 --- a/end2end-tests/tests/cucumber_tests.rs +++ b/end2end-tests/tests/cucumber_tests.rs @@ -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 +} diff --git a/runtime/common/src/fees.rs b/runtime/common/src/fees.rs index aec304d1d..cac38836f 100644 --- a/runtime/common/src/fees.rs +++ b/runtime/common/src/fees.rs @@ -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() } } -- GitLab