diff --git a/testing/jormungandr-scenario-tests/src/node.rs b/testing/jormungandr-scenario-tests/src/node.rs index b8448f6da4..84815b126c 100644 --- a/testing/jormungandr-scenario-tests/src/node.rs +++ b/testing/jormungandr-scenario-tests/src/node.rs @@ -13,6 +13,7 @@ use jormungandr_lib::{ NodeStatsDto, PeerRecord, PeerStats, }, }; +use jormungandr_testing_utils::testing::node::Explorer; pub use jormungandr_testing_utils::testing::{ network_builder::{ LeadershipMode, NodeAlias, NodeBlock0, NodeSetting, PersistenceMode, Settings, @@ -300,6 +301,10 @@ impl NodeController { .collect()) } + pub fn explorer(&self) -> Explorer { + Explorer::new(self.settings.config.rest.listen.clone().to_string()) + } + pub fn genesis_block_hash(&self) -> Result { Ok(block_on(self.grpc_client.get_genesis_block_hash())) } diff --git a/testing/jormungandr-scenario-tests/src/scenario/repository/mod.rs b/testing/jormungandr-scenario-tests/src/scenario/repository/mod.rs index fcdb017ec6..1c53659590 100644 --- a/testing/jormungandr-scenario-tests/src/scenario/repository/mod.rs +++ b/testing/jormungandr-scenario-tests/src/scenario/repository/mod.rs @@ -16,6 +16,7 @@ use crate::{ comm::passive_leader::*, features::{ leader_promotion::*, leadership_log::leader_restart_preserves_leadership_log, p2p::*, + vote::vote_e2e_flow, }, legacy, network::real::real_network, @@ -234,6 +235,12 @@ fn scenarios_repository() -> Vec { vec![Tag::Short], )); + repository.push(Scenario::new( + "vote_e2e_flow", + vote_e2e_flow, + vec![Tag::Short], + )); + repository.push(Scenario::new( "legacy_current_node_fragment_propagation", legacy::legacy_current_node_fragment_propagation, diff --git a/testing/jormungandr-scenario-tests/src/test/features/mod.rs b/testing/jormungandr-scenario-tests/src/test/features/mod.rs index aac441c466..f177d7d0cd 100644 --- a/testing/jormungandr-scenario-tests/src/test/features/mod.rs +++ b/testing/jormungandr-scenario-tests/src/test/features/mod.rs @@ -1,3 +1,4 @@ pub mod leader_promotion; pub mod leadership_log; pub mod p2p; +pub mod vote; diff --git a/testing/jormungandr-scenario-tests/src/test/features/vote.rs b/testing/jormungandr-scenario-tests/src/test/features/vote.rs new file mode 100644 index 0000000000..4f3704e6c0 --- /dev/null +++ b/testing/jormungandr-scenario-tests/src/test/features/vote.rs @@ -0,0 +1,202 @@ +use crate::{ + node::NodeController, + node::{LeadershipMode, PersistenceMode}, + test::{utils, Result}, + Context, ScenarioResult, +}; +use jormungandr_lib::interfaces::Explorer; +use jormungandr_testing_utils::testing::network_builder::SpawnParams; +use jormungandr_testing_utils::testing::node::time; +use jortestkit::process::sleep; +use rand_chacha::ChaChaRng; +use std::time::{Duration, SystemTime}; +const LEADER_1: &str = "Leader1"; +const LEADER_2: &str = "Leader2"; +const LEADER_3: &str = "Leader3"; +const LEADER_4: &str = "Leader4"; +const WALLET_NODE: &str = "Wallet_Node"; +use chain_impl_mockchain::vote::Choice; + +const DAVID_ADDRESS: &str = "DdzFFzCqrhsktawSMCWJJy3Dpp9BCjYPVecgsMb5U2G7d1ErUUmwSZvfSY3Yjn5njNadfwvebpVNS5cD4acEKSQih2sR76wx2kF4oLXT"; +const DAVID_MNEMONICS: &str = + "tired owner misery large dream glad upset welcome shuffle eagle pulp time"; + +const EDGAR_ADDRESS: &str = "DdzFFzCqrhsf2sWcZLzXhyLoLZcmw3Zf3UcJ2ozG1EKTwQ6wBY1wMG1tkXtPvEgvE5PKUFmoyzkP8BL4BwLmXuehjRHJtnPj73E5RPMx"; +const EDGAR_MNEMONICS: &str = + "edge club wrap where juice nephew whip entry cover bullet cause jeans"; + +const FILIP_MNEMONICS: &str = + "neck bulb teach illegal soul cry monitor claw amount boring provide village rival draft stone"; +const FILIP_ADDRESS: &str = "Ae2tdPwUPEZ8og5u4WF5rmSyme5Gvp8RYiLM2u7Vm8CyDQzLN3VYTN895Wk"; + +pub enum Vote { + BLANK = 0, + YES = 1, + NO = 2, +} + +pub fn vote_e2e_flow(mut context: Context) -> Result { + let scenario_settings = prepare_scenario! { + "Passive node promotion to leader", + &mut context, + topology [ + LEADER_1, + LEADER_2 -> LEADER_1, + LEADER_3 -> LEADER_1, + LEADER_4 -> LEADER_1, + WALLET_NODE -> LEADER_1,LEADER_2,LEADER_3,LEADER_4 + ] + blockchain { + consensus = Bft, + number_of_slots_per_epoch = 60, + slot_duration = 1, + leaders = [ LEADER_1, LEADER_2, LEADER_3, LEADER_4 ], + initials = [ + account "Alice" with 500_000_000, + ], + committees = [ "Alice" ], + legacy = [ + "David" address DAVID_ADDRESS mnemonics DAVID_MNEMONICS with 500_000_000, + "Edgar" address EDGAR_ADDRESS mnemonics EDGAR_MNEMONICS with 500_000_000, + "Filip" address FILIP_ADDRESS mnemonics FILIP_MNEMONICS with 500_000_000, + ], + vote_plans = [ + "fund1" from "Alice" through epochs 0->1->2 contains proposals = [ + proposal adds 100 to "rewards" with 3 vote options, + ] + ], + } + }; + + let mut controller = scenario_settings.build(context)?; + + let now = SystemTime::now(); + + // bootstrap network + let mut leader_1 = controller.spawn_node_custom( + SpawnParams::new(LEADER_1) + .leader() + .persistence_mode(PersistenceMode::Persistent) + .explorer(Explorer { enabled: true }), + )?; + leader_1.wait_for_bootstrap()?; + controller.monitor_nodes(); + + //start bft node 2 + let leader_2 = controller.spawn_node( + LEADER_2, + LeadershipMode::Leader, + PersistenceMode::Persistent, + )?; + leader_2.wait_for_bootstrap()?; + + //start bft node 3 + let leader_3 = controller.spawn_node( + LEADER_3, + LeadershipMode::Leader, + PersistenceMode::Persistent, + )?; + leader_3.wait_for_bootstrap()?; + + //start bft node 4 + let leader_4 = controller.spawn_node( + LEADER_4, + LeadershipMode::Leader, + PersistenceMode::Persistent, + )?; + leader_4.wait_for_bootstrap()?; + + // start passive node + let wallet_node = controller.spawn_node_custom( + SpawnParams::new(WALLET_NODE) + .passive() + .persistence_mode(PersistenceMode::Persistent) + .explorer(Explorer { enabled: true }), + )?; + wallet_node.wait_for_bootstrap()?; + + // start proxy and vit station + let vit_station = controller.spawn_vit_station()?; + let wallet_proxy = controller.spawn_wallet_proxy(WALLET_NODE)?; + + // start mainnet walets + let mut david = controller.iapyx_wallet("David", DAVID_MNEMONICS, &wallet_proxy)?; + david.retrieve_funds()?; + david.convert_and_send()?; + + let fund1_vote_plan = controller.vote_plan("fund1")?; + + // start voting + david.vote_for(fund1_vote_plan.id(), 0, Vote::YES as u8)?; + + let mut edgar = controller.iapyx_wallet("Edgar", EDGAR_MNEMONICS, &wallet_proxy)?; + edgar.retrieve_funds()?; + edgar.convert_and_send(); + + edgar.vote_for(fund1_vote_plan.id(), 0, Vote::YES as u8)?; + + let mut filip = controller.iapyx_wallet("Filip", FILIP_MNEMONICS, &wallet_proxy)?; + filip.retrieve_funds()?; + filip.convert_and_send(); + + filip.vote_for(fund1_vote_plan.id(), 0, Vote::NO as u8)?; + + time::wait_for_epoch(1, leader_1.explorer()); + + //tally the vote and observe changes + let rewards_before = leader_1 + .explorer() + .status() + .unwrap() + .data + .unwrap() + .status + .latest_block + .treasury + .unwrap() + .rewards + .parse::() + .unwrap(); + + let mut alice = controller.wallet("Alice")?; + controller.fragment_sender().send_vote_tally( + &mut alice, + &fund1_vote_plan.clone().into(), + &wallet_node, + )?; + + time::wait_for_epoch(2, leader_1.explorer()); + + let rewards_after = leader_1 + .explorer() + .status() + .unwrap() + .data + .unwrap() + .status + .latest_block + .treasury + .unwrap() + .rewards + .parse::() + .unwrap(); + + utils::assert_equals( + &rewards_before, + &(rewards_after - 100), + &format!( + "{} <> {} rewards were not increased", + rewards_before, rewards_after + ), + )?; + + wallet_node.shutdown()?; + vit_station.shutdown(); + wallet_proxy.shutdown(); + leader_4.shutdown()?; + leader_3.shutdown()?; + leader_2.shutdown()?; + leader_1.shutdown()?; + controller.finalize(); + Ok(ScenarioResult::passed()) +} diff --git a/testing/jormungandr-scenario-tests/src/test/mod.rs b/testing/jormungandr-scenario-tests/src/test/mod.rs index 31937513a9..e9ad536442 100644 --- a/testing/jormungandr-scenario-tests/src/test/mod.rs +++ b/testing/jormungandr-scenario-tests/src/test/mod.rs @@ -20,6 +20,7 @@ error_chain! { FragmentVerifier(jormungandr_testing_utils::testing::FragmentVerifierError); VerificationFailed(jormungandr_testing_utils::testing::VerificationError); MonitorResourcesError(jormungandr_testing_utils::testing::ConsumptionBenchmarkError); + WalletIapyxError(iapyx::ControllerError); } links {