From 983a37c01c8c0a5e285a6513db5a194dd7b9a275 Mon Sep 17 00:00:00 2001 From: Stefano Cunego <93382903+kukkok3@users.noreply.github.com> Date: Thu, 22 Sep 2022 13:08:10 +0200 Subject: [PATCH] NPG 3275 explorer vote plan test by id (#4090) * Create vote_plan_by_id.graphql * rename vote_plan_by_id file * adding vote plan query infra * adding vote plan test * update vote plan by id test * adding api call * adding vote plan not existing test * adding private vote test * clean up private testing test * add comment * adding vote_plan verifier * adding verifies to vote plan test * fix clippy --- .../explorer/graphql/voteplan_by_id.graphql | 40 + .../src/jormungandr/explorer/configuration.rs | 4 + .../src/jormungandr/explorer/data.rs | 10 +- .../src/jormungandr/explorer/mod.rs | 20 +- .../src/jormungandr/explorer/verifier.rs | 166 ++++- .../src/jormungandr/explorer/mod.rs | 1 + .../src/jormungandr/explorer/vote_plan.rs | 686 ++++++++++++++++++ 7 files changed, 919 insertions(+), 8 deletions(-) create mode 100644 testing/jormungandr-automation/resources/explorer/graphql/voteplan_by_id.graphql create mode 100644 testing/jormungandr-integration-tests/src/jormungandr/explorer/vote_plan.rs diff --git a/testing/jormungandr-automation/resources/explorer/graphql/voteplan_by_id.graphql b/testing/jormungandr-automation/resources/explorer/graphql/voteplan_by_id.graphql new file mode 100644 index 0000000000..c31de24198 --- /dev/null +++ b/testing/jormungandr-automation/resources/explorer/graphql/voteplan_by_id.graphql @@ -0,0 +1,40 @@ +query VotePlanById($id: String!){ + votePlan(id: $id) { + id + voteStart {...blockDate} + voteEnd {...blockDate} + committeeEnd {...blockDate} + payloadType + proposals { + proposalId + options {start end} + tally { + __typename + ... on TallyPublicStatus {results + options{start end} } + ... on TallyPrivateStatus {results + options{start end} } + } + votes{ + edges{ + node{ + address{ + id + #delegation{id} + } + payload{ + __typename + ... on VotePayloadPublicStatus {choice} + ... on VotePayloadPrivateStatus {proof encryptedVote} + } + } + } + totalCount + } + } + } +} +fragment blockDate on BlockDate{ + epoch{id} + slot + } diff --git a/testing/jormungandr-automation/src/jormungandr/explorer/configuration.rs b/testing/jormungandr-automation/src/jormungandr/explorer/configuration.rs index 142c40a1d9..553deb1736 100644 --- a/testing/jormungandr-automation/src/jormungandr/explorer/configuration.rs +++ b/testing/jormungandr-automation/src/jormungandr/explorer/configuration.rs @@ -21,6 +21,10 @@ impl ExplorerParams { #[allow(clippy::derivable_impls)] impl Default for ExplorerParams { + //Passing None we use the default values of the explorer + //DEFAULT_QUERY_DEPTH_LIMIT= 15 + //DEFAULT_QUERY_COMPLEXITY_LIMIT= 40 + //address_bech32_prefix= addr fn default() -> Self { ExplorerParams { query_complexity_limit: None, diff --git a/testing/jormungandr-automation/src/jormungandr/explorer/data.rs b/testing/jormungandr-automation/src/jormungandr/explorer/data.rs index 96361733c8..4821010b78 100644 --- a/testing/jormungandr-automation/src/jormungandr/explorer/data.rs +++ b/testing/jormungandr-automation/src/jormungandr/explorer/data.rs @@ -8,7 +8,7 @@ pub type NonZero = String; pub type TimeOffsetSeconds = String; pub type PublicKey = String; pub type ExternalProposalId = String; - +pub type Weight = String; use graphql_client::GraphQLQuery; #[derive(GraphQLQuery)] @@ -115,3 +115,11 @@ pub struct TransactionByIdCertificates; response_derives = "Debug" )] pub struct AllVotePlans; + +#[derive(GraphQLQuery)] +#[graphql( + query_path = "resources/explorer/graphql/voteplan_by_id.graphql", + schema_path = "resources/explorer/graphql/schema.graphql", + response_derives = "Debug,Clone" +)] +pub struct VotePlanById; diff --git a/testing/jormungandr-automation/src/jormungandr/explorer/mod.rs b/testing/jormungandr-automation/src/jormungandr/explorer/mod.rs index 1e90dfbfaf..902b081023 100644 --- a/testing/jormungandr-automation/src/jormungandr/explorer/mod.rs +++ b/testing/jormungandr-automation/src/jormungandr/explorer/mod.rs @@ -4,9 +4,9 @@ use self::{ data::{ address, all_blocks, all_stake_pools, all_vote_plans, block, blocks_by_chain_length, epoch, last_block, settings, stake_pool, transaction_by_id, transaction_by_id_certificates, - transactions_by_address, Address, AllBlocks, AllStakePools, AllVotePlans, Block, - BlocksByChainLength, Epoch, LastBlock, Settings, StakePool, TransactionById, - TransactionByIdCertificates, TransactionsByAddress, + transactions_by_address, vote_plan_by_id, Address, AllBlocks, AllStakePools, AllVotePlans, + Block, BlocksByChainLength, Epoch, LastBlock, Settings, StakePool, TransactionById, + TransactionByIdCertificates, TransactionsByAddress, VotePlanById, }, }; use crate::testing::configuration::get_explorer_app; @@ -19,7 +19,7 @@ use std::{ }; mod client; pub mod configuration; -mod data; +pub mod data; pub mod verifier; mod wrappers; @@ -307,6 +307,18 @@ impl Explorer { Ok(response_body) } + pub fn vote_plan( + &self, + id: String, + ) -> Result, ExplorerError> { + let query = VotePlanById::build_query(vote_plan_by_id::Variables { id }); + self.print_request(&query); + let response = self.client.run(query).map_err(ExplorerError::ClientError)?; + let response_body: Response = response.json()?; + self.print_log(&response_body); + Ok(response_body) + } + pub fn transaction( &self, hash: Hash, diff --git a/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs b/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs index 086f1b4f2f..0f092975ef 100644 --- a/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs +++ b/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs @@ -1,8 +1,12 @@ use super::data::{ address::AddressAddress, settings::SettingsSettingsFees, transaction_by_id_certificates::*, transactions_by_address::TransactionsByAddressTipTransactionsByAddress, + vote_plan_by_id::VotePlanByIdVotePlan, +}; +use crate::jormungandr::explorer::{ + data::{transaction_by_id_certificates::PayloadType as expPayloadType, vote_plan_by_id}, + vote_plan_by_id::VotePlanByIdVotePlanProposalsTally::*, }; -use crate::jormungandr::explorer::data::transaction_by_id_certificates::PayloadType as expPayloadType; use bech32::FromBase32; use chain_addr::AddressReadable; use chain_crypto::{Ed25519, PublicKey}; @@ -13,12 +17,17 @@ use chain_impl_mockchain::{ config::{ConfigParam::*, RewardParams}, fee::LinearFee, fragment::Fragment, + testing::data::Wallet, transaction::{AccountIdentifier, InputEnum, Transaction}, - vote::PayloadType, + vote, + vote::{Choice, PayloadType}, +}; +use jormungandr_lib::interfaces::{ + Address, FragmentStatus, PrivateTallyState, Tally, VotePlanStatus, }; -use jormungandr_lib::interfaces::{Address, FragmentStatus}; use std::{collections::HashMap, num::NonZeroU64}; use thiserror::Error; +use vote_plan_by_id::VotePlanByIdVotePlanProposalsVotesEdgesNodePayload::*; #[derive(Debug, Error)] pub enum VerifierError { @@ -958,6 +967,157 @@ impl ExplorerVerifier { } } + pub fn assert_vote_plan_by_id( + explorer_vote_plan: VotePlanByIdVotePlan, + vote_plan_status: VotePlanStatus, + proposal_votes: HashMap>, + ) { + assert_eq!(explorer_vote_plan.id, vote_plan_status.id.to_string()); + assert_eq!( + explorer_vote_plan.vote_start.epoch.id, + vote_plan_status.vote_start.epoch().to_string() + ); + assert_eq!( + explorer_vote_plan.vote_start.slot, + vote_plan_status.vote_start.slot().to_string() + ); + assert_eq!( + explorer_vote_plan.vote_end.epoch.id, + vote_plan_status.vote_end.epoch().to_string() + ); + assert_eq!( + explorer_vote_plan.vote_end.slot, + vote_plan_status.vote_end.slot().to_string() + ); + assert_eq!( + explorer_vote_plan.committee_end.epoch.id, + vote_plan_status.committee_end.epoch().to_string() + ); + assert_eq!( + explorer_vote_plan.committee_end.slot, + vote_plan_status.committee_end.slot().to_string() + ); + match explorer_vote_plan.payload_type { + vote_plan_by_id::PayloadType::PUBLIC => assert!(matches!( + vote_plan_status.payload, + vote::PayloadType::Public + )), + vote_plan_by_id::PayloadType::PRIVATE => assert!(matches!( + vote_plan_status.payload, + vote::PayloadType::Private + )), + vote_plan_by_id::PayloadType::Other(_) => panic!("Wrong payload type"), + } + + assert_eq!( + explorer_vote_plan.proposals.len(), + vote_plan_status.proposals.len() + ); + for explorer_proposal in explorer_vote_plan.proposals { + let vote_proposal_status = + match vote_plan_status.proposals.iter().position(|proposal| { + explorer_proposal.proposal_id == proposal.proposal_id.to_string() + }) { + Some(index) => vote_plan_status.proposals[index].clone(), + None => panic!("Proposal id not found"), + }; + assert_eq!( + vote_proposal_status.options.start, + explorer_proposal.options.start as u8 + ); + assert_eq!( + vote_proposal_status.options.end, + explorer_proposal.options.end as u8 + ); + match &vote_proposal_status.tally { + Tally::Public { result } => { + if let TallyPublicStatus(explorer_tally_status) = + explorer_proposal.tally.unwrap() + { + assert_eq!(result.results.len(), explorer_tally_status.results.len()); + let matching_results = result + .results + .iter() + .zip(explorer_tally_status.results.iter()) + .filter(|&(a, b)| &a.to_string() == b) + .count(); + assert_eq!(matching_results, result.results.len()); + assert_eq!(result.options.len(), explorer_tally_status.results.len()); + assert_eq!( + result.options.start, + explorer_tally_status.options.start as u8 + ); + assert_eq!(result.options.end, explorer_tally_status.options.end as u8); + } else { + panic!("Wrong tally status. Expected Public") + } + } + Tally::Private { state } => { + assert!(explorer_proposal.tally.is_some()); + if let TallyPrivateStatus(explorer_tally_status) = + explorer_proposal.tally.unwrap() + { + match state { + PrivateTallyState::Encrypted { encrypted_tally: _ } => assert!( + explorer_tally_status.results.is_none(), + "BUG NPG-3369 fixed" + ), + PrivateTallyState::Decrypted { result } => { + let explorer_tally_result = explorer_tally_status.results.unwrap(); + assert_eq!(result.results.len(), explorer_tally_result.len()); + let matching_results = result + .results + .iter() + .zip(explorer_tally_result.iter()) + .filter(|&(a, b)| &a.to_string() == b) + .count(); + assert_eq!(matching_results, result.results.len()); + assert_eq!(result.options.len(), explorer_tally_result.len()); + assert_eq!( + result.options.start, + explorer_tally_status.options.start as u8 + ); + assert_eq!( + result.options.end, + explorer_tally_status.options.end as u8 + ); + } + } + } else { + panic!("Wrong tally status. Expected Private") + } + } + } + assert_eq!( + vote_proposal_status.votes_cast, + explorer_proposal.votes.total_count as usize + ); + if vote_proposal_status.votes_cast == 0 { + assert!(explorer_proposal.votes.edges.unwrap().is_empty()); + } else { + let explorer_votes = explorer_proposal.votes.edges.unwrap(); + assert_eq!(explorer_votes.len(), vote_proposal_status.votes_cast); + let votes = proposal_votes + .get(&vote_proposal_status.proposal_id.to_string()) + .unwrap(); + for vote in votes { + for explorer_vote in &explorer_votes { + if vote.0.public_key().to_string() + == explorer_vote.as_ref().unwrap().node.address.id + { + match &explorer_vote.as_ref().unwrap().node.payload { + VotePayloadPublicStatus(choice) => { + assert_eq!(choice.choice as u8, vote.1.as_byte()) + } + VotePayloadPrivateStatus(_) => todo!(), + } + } + } + } + } + } + } + fn decode_bech32_pk(bech32_public_key: &str) -> PublicKey { let (_, data, _variant) = bech32::decode(bech32_public_key).unwrap(); let dat = Vec::from_base32(&data).unwrap(); diff --git a/testing/jormungandr-integration-tests/src/jormungandr/explorer/mod.rs b/testing/jormungandr-integration-tests/src/jormungandr/explorer/mod.rs index 8e3e85d139..9e4bac3d2b 100644 --- a/testing/jormungandr-integration-tests/src/jormungandr/explorer/mod.rs +++ b/testing/jormungandr-integration-tests/src/jormungandr/explorer/mod.rs @@ -3,3 +3,4 @@ pub mod certificates; pub mod explorer_sanity; pub mod settings; pub mod transaction; +pub mod vote_plan; diff --git a/testing/jormungandr-integration-tests/src/jormungandr/explorer/vote_plan.rs b/testing/jormungandr-integration-tests/src/jormungandr/explorer/vote_plan.rs new file mode 100644 index 0000000000..439994a958 --- /dev/null +++ b/testing/jormungandr-integration-tests/src/jormungandr/explorer/vote_plan.rs @@ -0,0 +1,686 @@ +use assert_fs::TempDir; +use chain_addr::Discrimination; +use chain_core::property::BlockDate as propertyBlockDate; +use chain_impl_mockchain::{ + block::BlockDate, certificate::VoteAction, chaintypes::ConsensusType, + ledger::governance::TreasuryGovernanceAction, testing::data::Wallet as chainWallet, + tokens::minting_policy::MintingPolicy, value::Value, vote::Choice, +}; +use jormungandr_automation::{ + jormungandr::{ + explorer::{configuration::ExplorerParams, verifier::ExplorerVerifier}, + ConfigurationBuilder, Starter, + }, + testing::{ + time::{get_current_date, wait_for_date}, + VotePlanBuilder, + }, +}; +use jormungandr_lib::interfaces::{InitialToken, KesUpdateSpeed}; +use rand_core::OsRng; +use std::collections::HashMap; +use thor::{ + vote_plan_cert, FragmentBuilder, FragmentSender, FragmentSenderSetup, + PrivateVoteCommitteeDataManager, Wallet, +}; + +const INITIAL_FUND_PER_WALLET_1: u64 = 1_000_000; +const INITIAL_FUND_PER_WALLET_2: u64 = 2_000_000; +const INITIAL_TREASURY: u64 = 1000; +const REWARD_INCREASE: u64 = 10; +const SLOTS_PER_EPOCH: u32 = 20; +const SLOT_DURATION: u8 = 4; + +const VOTE_PLAN_QUERY_COMPLEXITY_LIMIT: u64 = 50; +const VOTE_PLAN_QUERY_DEPTH_LIMIT: u64 = 30; +const VOTE_FOR_MARIO: u8 = 0; +const VOTE_FOR_LUIGI: u8 = 1; +const VOTE_FOR_ANTONIO: u8 = 2; + +#[test] +pub fn explorer_vote_plan_not_existing() { + let temp_dir = TempDir::new().unwrap(); + let alice = Wallet::default(); + let proposals = vec![VOTE_FOR_ANTONIO]; + let proposal_count = proposals.len(); + + let vote_plan = VotePlanBuilder::new() + .proposals_count(proposal_count) + .vote_start(BlockDate::from_epoch_slot_id(0, 0)) + .tally_start(BlockDate::from_epoch_slot_id(1, 0)) + .tally_end(BlockDate::from_epoch_slot_id(1, 10)) + .public() + .build(); + + let config = ConfigurationBuilder::new() + .with_funds(vec![alice.to_initial_fund(INITIAL_FUND_PER_WALLET_1)]) + .with_token(InitialToken { + token_id: vote_plan.voting_token().clone().into(), + policy: MintingPolicy::new().into(), + to: vec![alice.to_initial_token(INITIAL_FUND_PER_WALLET_1)], + }) + .with_committees(&[alice.to_committee_id()]) + .with_slots_per_epoch(SLOTS_PER_EPOCH) + .with_treasury(INITIAL_TREASURY.into()) + .build(&temp_dir); + + let jormungandr = Starter::new() + .config(config) + .temp_dir(temp_dir) + .start() + .unwrap(); + + let params = ExplorerParams::new( + VOTE_PLAN_QUERY_COMPLEXITY_LIMIT, + VOTE_PLAN_QUERY_DEPTH_LIMIT, + None, + ); + let explorer_process = jormungandr.explorer(params); + let explorer = explorer_process.client(); + + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.data.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + assert!( + query_response.errors.is_some(), + "{:?}", + query_response.errors.unwrap() + ); + + assert!( + &query_response + .errors + .as_ref() + .unwrap() + .last() + .unwrap() + .message + .contains("not found"), + "{:?}", + query_response.errors.unwrap() + ); +} + +#[should_panic] +#[test] //NPG-3334 +pub fn explorer_vote_plan_public_flow_test() { + let temp_dir = TempDir::new().unwrap(); + let alice = Wallet::default(); + let bob = Wallet::default(); + let mut voters = vec![alice, bob]; + let proposals = vec![VOTE_FOR_MARIO, VOTE_FOR_LUIGI, VOTE_FOR_ANTONIO]; + let proposal_count = proposals.len(); + let yes_choice = Choice::new(1); + let no_choice = Choice::new(0); + let mut vote_for_mario: Vec<(chainWallet, Choice)> = Vec::new(); + let mut vote_for_luigi: Vec<(chainWallet, Choice)> = Vec::new(); + let mut vote_for_antonio: Vec<(chainWallet, Choice)> = Vec::new(); + let mut proposal_votes: HashMap> = HashMap::new(); + + let vote_plan = VotePlanBuilder::new() + .proposals_count(proposal_count) + .vote_start(BlockDate::from_epoch_slot_id(0, 0)) + .tally_start(BlockDate::from_epoch_slot_id(1, 0)) + .tally_end(BlockDate::from_epoch_slot_id(1, 10)) + .public() + .build(); + + let vote_plan_cert = vote_plan_cert( + &voters[0], + chain_impl_mockchain::block::BlockDate { + epoch: 1, + slot_id: 0, + }, + &vote_plan, + ) + .into(); + + let config = ConfigurationBuilder::new() + .with_funds( + voters + .iter() + .map(|x| x.to_initial_fund(INITIAL_TREASURY)) + .collect(), + ) + .with_token(InitialToken { + token_id: vote_plan.voting_token().clone().into(), + policy: MintingPolicy::new().into(), + to: vec![ + voters[0].to_initial_token(INITIAL_FUND_PER_WALLET_1), + voters[1].to_initial_token(INITIAL_FUND_PER_WALLET_2), + ], + }) + .with_committees(&[voters[0].to_committee_id()]) + .with_slots_per_epoch(SLOTS_PER_EPOCH) + .with_certs(vec![vote_plan_cert]) + .with_treasury(INITIAL_TREASURY.into()) + .build(&temp_dir); + + let jormungandr = Starter::new() + .config(config) + .temp_dir(temp_dir) + .start() + .unwrap(); + + let transaction_sender = FragmentSender::new( + jormungandr.genesis_block_hash(), + jormungandr.fees(), + chain_impl_mockchain::block::BlockDate { + epoch: 3, + slot_id: 0, + } + .into(), + FragmentSenderSetup::resend_3_times(), + ); + + let params = ExplorerParams::new( + VOTE_PLAN_QUERY_COMPLEXITY_LIMIT, + VOTE_PLAN_QUERY_DEPTH_LIMIT, + None, + ); + let explorer_process = jormungandr.explorer(params); + let explorer = explorer_process.client(); + + //1.Vote plan started + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.errors.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + let vote_plan_transaction = query_response.data.unwrap().vote_plan; + let vote_plan_status = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + ExplorerVerifier::assert_vote_plan_by_id( + vote_plan_transaction, + vote_plan_status, + proposal_votes.clone(), + ); + + assert!(vote_plan.can_vote(get_current_date(&mut jormungandr.rest()).into())); + + //2. Voting + transaction_sender + .send_vote_cast( + &mut voters[0], + &vote_plan, + VOTE_FOR_ANTONIO, + &no_choice, + &jormungandr, + ) + .unwrap(); + + vote_for_antonio.push((chainWallet::from(voters[0].clone()), no_choice)); + + transaction_sender + .send_vote_cast( + &mut voters[1], + &vote_plan, + VOTE_FOR_ANTONIO, + &yes_choice, + &jormungandr, + ) + .unwrap(); + + vote_for_antonio.push((chainWallet::from(voters[1].clone()), yes_choice)); + + transaction_sender + .send_vote_cast( + &mut voters[0], + &vote_plan, + VOTE_FOR_MARIO, + &no_choice, + &jormungandr, + ) + .unwrap(); + + vote_for_mario.push((chainWallet::from(voters[0].clone()), no_choice)); + + transaction_sender + .send_vote_cast( + &mut voters[1], + &vote_plan, + VOTE_FOR_LUIGI, + &no_choice, + &jormungandr, + ) + .unwrap(); + + vote_for_luigi.push((chainWallet::from(voters[1].clone()), no_choice)); + + proposal_votes.insert( + vote_plan + .proposals() + .to_vec() + .get(VOTE_FOR_MARIO as usize) + .unwrap() + .external_id() + .to_string(), + vote_for_mario, + ); + proposal_votes.insert( + vote_plan + .proposals() + .to_vec() + .get(VOTE_FOR_ANTONIO as usize) + .unwrap() + .external_id() + .to_string(), + vote_for_antonio, + ); + proposal_votes.insert( + vote_plan + .proposals() + .to_vec() + .get(VOTE_FOR_LUIGI as usize) + .unwrap() + .external_id() + .to_string(), + vote_for_luigi, + ); + + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.errors.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + let vote_plan_transaction = query_response.data.unwrap().vote_plan; + let vote_plan_status = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + ExplorerVerifier::assert_vote_plan_by_id( + vote_plan_transaction, + vote_plan_status, + proposal_votes.clone(), + ); + + wait_for_date(vote_plan.vote_end().into(), jormungandr.rest()); + + //3.Start talling + transaction_sender + .send_public_vote_tally(&mut voters[0], &vote_plan, &jormungandr) + .unwrap(); + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.errors.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + let vote_plan_transaction = query_response.data.unwrap().vote_plan; + let vote_plan_status = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + ExplorerVerifier::assert_vote_plan_by_id( + vote_plan_transaction, + vote_plan_status, + proposal_votes.clone(), + ); + + wait_for_date(vote_plan.committee_end().into(), jormungandr.rest()); + + //4. End talling + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.errors.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + let vote_plan_transaction = query_response.data.unwrap().vote_plan; + let vote_plan_status = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + ExplorerVerifier::assert_vote_plan_by_id( + vote_plan_transaction, + vote_plan_status, + proposal_votes.clone(), + ); +} + +#[should_panic] +#[test] //NPG-3369 +pub fn explorer_vote_plan_private_flow_test() { + let temp_dir = TempDir::new().unwrap().into_persistent(); + let yes_choice = Choice::new(1); + let no_choice = Choice::new(2); + let threshold = 1; + let mut rng = OsRng; + let alice = Wallet::new_account_with_discrimination(&mut rng, Discrimination::Production); + let bob = Wallet::new_account_with_discrimination(&mut rng, Discrimination::Production); + let mut voters = vec![alice, bob]; + let proposals = vec![VOTE_FOR_MARIO, VOTE_FOR_LUIGI, VOTE_FOR_ANTONIO]; + let proposal_count = proposals.len(); + let private_vote_committee_data_manager = PrivateVoteCommitteeDataManager::new( + &mut OsRng, + vec![("Alice".to_owned(), voters[0].account_id())], + threshold, + ); + let mut vote_for_mario: Vec<(chainWallet, Choice)> = Vec::new(); + let mut vote_for_luigi: Vec<(chainWallet, Choice)> = Vec::new(); + let vote_for_antonio: Vec<(chainWallet, Choice)> = Vec::new(); + let mut proposal_votes: HashMap> = HashMap::new(); + + let vote_plan = VotePlanBuilder::new() + .proposals_count(proposal_count) + .action_type(VoteAction::Treasury { + action: TreasuryGovernanceAction::TransferToRewards { + value: Value(REWARD_INCREASE), + }, + }) + .private() + .vote_start(BlockDate::from_epoch_slot_id(0, 0)) + .tally_start(BlockDate::from_epoch_slot_id(1, 0)) + .tally_end(BlockDate::from_epoch_slot_id(1, 10)) + .member_public_keys(private_vote_committee_data_manager.member_public_keys()) + .options_size(3) + .build(); + + let vote_plan_cert = vote_plan_cert( + &voters[0], + chain_impl_mockchain::block::BlockDate { + epoch: 1, + slot_id: 0, + }, + &vote_plan, + ) + .into(); + + let config = ConfigurationBuilder::new() + .with_funds(vec![ + voters[0].to_initial_fund(INITIAL_FUND_PER_WALLET_1), + voters[1].to_initial_fund(INITIAL_FUND_PER_WALLET_2), + ]) + .with_token(InitialToken { + token_id: vote_plan.voting_token().clone().into(), + policy: MintingPolicy::new().into(), + to: vec![ + voters[0].to_initial_token(INITIAL_FUND_PER_WALLET_1), + voters[1].to_initial_token(INITIAL_FUND_PER_WALLET_2), + ], + }) + .with_block0_consensus(ConsensusType::Bft) + .with_kes_update_speed(KesUpdateSpeed::MAXIMUM) + .with_treasury(INITIAL_TREASURY.into()) + .with_discrimination(Discrimination::Production) + .with_committees(&[voters[0].to_committee_id()]) + .with_slot_duration(SLOT_DURATION) + .with_slots_per_epoch(SLOTS_PER_EPOCH) + .with_certs(vec![vote_plan_cert]) + .build(&temp_dir); + + let jormungandr = Starter::new().config(config).start().unwrap(); + + let transaction_sender = FragmentSender::new( + jormungandr.genesis_block_hash(), + jormungandr.fees(), + chain_impl_mockchain::block::BlockDate { + epoch: 1, + slot_id: 0, + } + .into(), + FragmentSenderSetup::resend_3_times(), + ); + + let fragment_builder = FragmentBuilder::new( + &jormungandr.genesis_block_hash(), + &jormungandr.fees(), + chain_impl_mockchain::block::BlockDate { + epoch: 3, + slot_id: 0, + }, + ); + + let params = ExplorerParams::new( + VOTE_PLAN_QUERY_COMPLEXITY_LIMIT, + VOTE_PLAN_QUERY_DEPTH_LIMIT, + None, + ); + let explorer_process = jormungandr.explorer(params); + let explorer = explorer_process.client(); + + let rewards_before: u64 = jormungandr.rest().remaining_rewards().unwrap().into(); + + //1. Voteplan + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.errors.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + let vote_plan_transaction = query_response.data.unwrap().vote_plan; + let vote_plan_status = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + ExplorerVerifier::assert_vote_plan_by_id( + vote_plan_transaction, + vote_plan_status, + proposal_votes.clone(), + ); + + //2. Voting + assert!(vote_plan.can_vote(get_current_date(&mut jormungandr.rest()).into())); + + let first_voter_luigi_fragment = + fragment_builder.private_vote_cast(&voters[0], &vote_plan, VOTE_FOR_LUIGI, &yes_choice); + + let second_voter_luigi_fragment = + fragment_builder.private_vote_cast(&voters[1], &vote_plan, VOTE_FOR_LUIGI, &yes_choice); + voters[1].confirm_transaction(); + + let second_voter_mario_fragment = + fragment_builder.private_vote_cast(&voters[1], &vote_plan, VOTE_FOR_MARIO, &no_choice); + + transaction_sender + .send_fragment(&mut voters[0], first_voter_luigi_fragment, &jormungandr) + .unwrap(); + + vote_for_luigi.push((chainWallet::from(voters[0].clone()), yes_choice)); + + transaction_sender + .send_fragment(&mut voters[1], second_voter_luigi_fragment, &jormungandr) + .unwrap(); + + vote_for_luigi.push((chainWallet::from(voters[1].clone()), yes_choice)); + + transaction_sender + .send_fragment(&mut voters[1], second_voter_mario_fragment, &jormungandr) + .unwrap(); + + vote_for_mario.push((chainWallet::from(voters[1].clone()), no_choice)); + + proposal_votes.insert( + vote_plan + .proposals() + .to_vec() + .get(VOTE_FOR_MARIO as usize) + .unwrap() + .external_id() + .to_string(), + vote_for_mario, + ); + + proposal_votes.insert( + vote_plan + .proposals() + .to_vec() + .get(VOTE_FOR_ANTONIO as usize) + .unwrap() + .external_id() + .to_string(), + vote_for_antonio, + ); + + proposal_votes.insert( + vote_plan + .proposals() + .to_vec() + .get(VOTE_FOR_LUIGI as usize) + .unwrap() + .external_id() + .to_string(), + vote_for_luigi, + ); + + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.errors.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + let vote_plan_transaction = query_response.data.unwrap().vote_plan; + let vote_plan_status = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + ExplorerVerifier::assert_vote_plan_by_id( + vote_plan_transaction, + vote_plan_status, + proposal_votes.clone(), + ); + + //3.Tally + wait_for_date(vote_plan.committee_start().into(), jormungandr.rest()); + let transaction_sender = + transaction_sender.set_valid_until(chain_impl_mockchain::block::BlockDate { + epoch: 3, + slot_id: 0, + }); + + let vote_plan_statuses = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + let decrypted_shares = private_vote_committee_data_manager + .decrypt_tally(&vote_plan_statuses.into()) + .unwrap(); + + let _mempool_check = transaction_sender + .send_private_vote_tally(&mut voters[0], &vote_plan, decrypted_shares, &jormungandr) + .unwrap(); + + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.errors.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + let vote_plan_transaction = query_response.data.unwrap().vote_plan; + let vote_plan_status = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + ExplorerVerifier::assert_vote_plan_by_id( + vote_plan_transaction, + vote_plan_status, + proposal_votes.clone(), + ); + + //4. Tally end + wait_for_date(vote_plan.committee_end().into(), jormungandr.rest()); + + let rewards_after: u64 = jormungandr.rest().remaining_rewards().unwrap().into(); + + // We want to make sure that our small rewards increase is reflected in current rewards amount + assert!( + rewards_after == rewards_before + REWARD_INCREASE, + "Vote was unsuccessful" + ); + + let query_response = explorer + .vote_plan(vote_plan.to_id().to_string()) + .expect("vote plan transaction not found"); + + assert!( + query_response.errors.is_none(), + "{:?}", + query_response.errors.unwrap() + ); + + let vote_plan_transaction = query_response.data.unwrap().vote_plan; + let vote_plan_status = jormungandr + .rest() + .vote_plan_statuses() + .unwrap() + .first() + .unwrap() + .clone(); + + ExplorerVerifier::assert_vote_plan_by_id( + vote_plan_transaction, + vote_plan_status, + proposal_votes.clone(), + ); +}