diff --git a/testing/jormungandr-automation/resources/explorer/graphql/transaction_by_id_certificates.graphql b/testing/jormungandr-automation/resources/explorer/graphql/transaction_by_id_certificates.graphql index 6b2c22182e..7460a4dcc5 100644 --- a/testing/jormungandr-automation/resources/explorer/graphql/transaction_by_id_certificates.graphql +++ b/testing/jormungandr-automation/resources/explorer/graphql/transaction_by_id_certificates.graphql @@ -29,7 +29,7 @@ query TransactionByIdCertificates($id: String!){ } ... on VoteCast {votePlan proposalIndex} ... on VoteTally {votePlan} - ... on UpdateProposal {changes {...configParams} + ... on UpdateProposal {changes { configParams {...configParam}} proposerId{id} } ... on UpdateVote{proposalId voterId{id}} @@ -50,8 +50,82 @@ fragment blockDate on BlockDate{ slot } -fragment configParams on ConfigParams +fragment configParam on ConfigParam { __typename - #Block0Date | Discrimination | ConsensusType | SlotsPerEpoch | SlotDuration | EpochStabilityDepth | Milli | BlockContentMaxSize | AddBftLeader | RemoveBftLeader | LinearFee | ProposalExpiration | KesUpdateSpeed | TreasuryAdd | TreasuryParams | RewardPot | RewardParams | PerCertificateFee | FeesInTreasury | RewardLimitNone | RewardLimitByAbsoluteStake | PoolRewardParticipationCapping | AddCommitteeId | RemoveCommitteeId | PerVoteCertificateFee | TransactionMaxExpiryEpochs + ... on Block0Date { block0Date } + ... on Discrimination { discrimination } + ... on ConsensusType { consensusType } + ... on SlotsPerEpoch { slotsPerEpoch } + ... on SlotDuration { slotDuration} + ... on EpochStabilityDepth { epochStabilityDepth } + ... on Milli { milli} + ... on BlockContentMaxSize { blockContentMaxSize} + ... on AddBftLeader { addBftLeader{ id }} + ... on RemoveBftLeader { removeBftLeader { id }} + ... on LinearFee { + constant + coefficient + certificate + perCertificateFees { + certificatePoolRegistration + certificateStakeDelegation + certificateOwnerStakeDelegation + } + perVoteCertificateFees { + certificateVotePlan + certificateVoteCast + }} + ... on ProposalExpiration{ proposalExpiration } + ... on KesUpdateSpeed { kesUpdateSpeed} + ... on TreasuryAdd { treasuryAdd } + ... on TreasuryParams { treasuryParams { + fixed + ratio { + numerator + denominator + } + maxLimit + }} + ... on RewardPot { rewardPot } + ... on RewardParams { rewardParams { + __typename + ... on LinearRewardParams{ + constant + ratio { + numerator + denominator + } + epochStart + epochRate + } + ... on HalvingRewardParams { + constant + ratio { + numerator + denominator + } + epochStart + epochRate + } + }} + ... on PerCertificateFee{ + certificatePoolRegistration + certificateStakeDelegation + certificateOwnerStakeDelegation + } + ... on FeesInTreasury { feesInTreasury} + ... on RewardLimitNone { rewardLimitNone } + ... on RewardLimitByAbsoluteStake { rewardLimitByAbsoluteStake { + numerator + denominator + }} + ... on PoolRewardParticipationCapping { min max } + ... on AddCommitteeId { addCommitteeId} + ... on RemoveCommitteeId { removeCommitteeId } + ... on PerVoteCertificateFee { + certificateVotePlan + certificateVoteCast + } + ... on TransactionMaxExpiryEpochs { transactionMaxExpiryEpochs } } diff --git a/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs b/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs index 599decd187..a343114236 100644 --- a/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs +++ b/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs @@ -1,25 +1,17 @@ -use super::data::{ - settings::SettingsSettingsFees, - transaction_by_id_certificates::{ - TransactionByIdCertificatesTransaction, TransactionByIdCertificatesTransactionCertificate, - TransactionByIdCertificatesTransactionCertificateOnOwnerStakeDelegation, - TransactionByIdCertificatesTransactionCertificateOnPoolRegistration, - TransactionByIdCertificatesTransactionCertificateOnPoolRetirement, - TransactionByIdCertificatesTransactionCertificateOnPoolUpdate, - TransactionByIdCertificatesTransactionCertificateOnStakeDelegation, - }, -}; +use super::data::{settings::SettingsSettingsFees, transaction_by_id_certificates::*}; +use crate::jormungandr::explorer::data::transaction_by_id_certificates::PayloadType as expPayloadType; use bech32::FromBase32; use chain_addr::AddressReadable; use chain_crypto::{Ed25519, PublicKey}; use chain_impl_mockchain::{ account::DelegationType, - certificate::{ - OwnerStakeDelegation, PoolRegistration, PoolRetirement, PoolUpdate, StakeDelegation, - }, + certificate::*, + chaintypes::ConsensusType, + config::{ConfigParam::*, RewardParams}, fee::LinearFee, fragment::Fragment, transaction::{AccountIdentifier, InputEnum, Transaction}, + vote::PayloadType, }; use std::num::NonZeroU64; use thiserror::Error; @@ -136,13 +128,92 @@ impl ExplorerVerifier { }) } } - TransactionByIdCertificatesTransactionCertificate::VotePlan(_) => todo!(), - TransactionByIdCertificatesTransactionCertificate::VoteCast(_) => todo!(), - TransactionByIdCertificatesTransactionCertificate::VoteTally(_) => todo!(), - TransactionByIdCertificatesTransactionCertificate::UpdateProposal(_) => todo!(), - TransactionByIdCertificatesTransactionCertificate::UpdateVote(_) => todo!(), - TransactionByIdCertificatesTransactionCertificate::MintToken(_) => todo!(), - TransactionByIdCertificatesTransactionCertificate::EvmMapping(_) => todo!(), + TransactionByIdCertificatesTransactionCertificate::VotePlan(explorer_cert) => { + if let Fragment::VotePlan(fragment_cert) = fragment { + Self::assert_transaction_params( + fragment_cert.clone(), + explorer_transaction.clone(), + ) + .unwrap(); + Self::assert_vote_plan(fragment_cert, explorer_cert.clone()); + Ok(()) + } else { + Err(VerifierError::InvalidCertificate { + received: "VotePlan".to_string(), + }) + } + } + TransactionByIdCertificatesTransactionCertificate::VoteCast(explorer_cert) => { + if let Fragment::VoteCast(fragment_cert) = fragment { + Self::assert_transaction_params( + fragment_cert.clone(), + explorer_transaction.clone(), + ) + .unwrap(); + Self::assert_vote_cast(fragment_cert, explorer_cert.clone()); + Ok(()) + } else { + Err(VerifierError::InvalidCertificate { + received: "VoteCast".to_string(), + }) + } + } + TransactionByIdCertificatesTransactionCertificate::VoteTally(explorer_cert) => { + if let Fragment::VoteTally(fragment_cert) = fragment { + Self::assert_transaction_params( + fragment_cert.clone(), + explorer_transaction.clone(), + ) + .unwrap(); + Self::assert_vote_tally(fragment_cert, explorer_cert.clone()); + Ok(()) + } else { + Err(VerifierError::InvalidCertificate { + received: "VoteTally".to_string(), + }) + } + } + TransactionByIdCertificatesTransactionCertificate::UpdateProposal( + explorer_cert, + ) => { + if let Fragment::UpdateProposal(fragment_cert) = fragment { + Self::assert_transaction_params( + fragment_cert.clone(), + explorer_transaction.clone(), + ) + .unwrap(); + Self::assert_update_proposal(fragment_cert, explorer_cert.clone()); + Ok(()) + } else { + Err(VerifierError::InvalidCertificate { + received: "UpdateProposal".to_string(), + }) + } + } + TransactionByIdCertificatesTransactionCertificate::UpdateVote(explorer_cert) => { + if let Fragment::UpdateVote(fragment_cert) = fragment { + Self::assert_transaction_params( + fragment_cert.clone(), + explorer_transaction.clone(), + ) + .unwrap(); + Self::assert_update_vote(fragment_cert, explorer_cert.clone()); + Ok(()) + } else { + Err(VerifierError::InvalidCertificate { + received: "UpdateVote".to_string(), + }) + } + } + TransactionByIdCertificatesTransactionCertificate::MintToken(_) => { + Err(VerifierError::InvalidCertificate { + received: "MintToken can be only in block0".to_string(), + }) + } + TransactionByIdCertificatesTransactionCertificate::EvmMapping(_) => { + //Not implemented because of the bug EAS-238 + Err(VerifierError::Unimplemented) + } } } } @@ -391,6 +462,395 @@ impl ExplorerVerifier { ); } + fn assert_vote_plan( + fragment_cert: Transaction, + explorer_cert: TransactionByIdCertificatesTransactionCertificateOnVotePlan, + ) { + let vote_plan_cert = fragment_cert.as_slice().payload().into_payload(); + assert_eq!( + explorer_cert.vote_start.epoch.id.parse::().unwrap(), + vote_plan_cert.vote_start().epoch + ); + assert_eq!( + explorer_cert.vote_start.slot.parse::().unwrap(), + vote_plan_cert.vote_start().slot_id + ); + assert_eq!( + explorer_cert.vote_end.epoch.id.parse::().unwrap(), + vote_plan_cert.vote_end().epoch + ); + assert_eq!( + explorer_cert.vote_end.slot.parse::().unwrap(), + vote_plan_cert.vote_end().slot_id + ); + assert_eq!( + explorer_cert.committee_end.epoch.id.parse::().unwrap(), + vote_plan_cert.committee_end().epoch + ); + assert_eq!( + explorer_cert.committee_end.slot.parse::().unwrap(), + vote_plan_cert.committee_end().slot_id + ); + + match vote_plan_cert.payload_type() { + PayloadType::Public => { + assert!(matches!(explorer_cert.payload_type, expPayloadType::PUBLIC)) + } + PayloadType::Private => assert!(matches!( + explorer_cert.payload_type, + expPayloadType::PRIVATE + )), + } + + assert_eq!( + explorer_cert.proposals.len(), + vote_plan_cert.proposals().len() + ); + let matching_proposal = explorer_cert + .proposals + .iter() + .zip(vote_plan_cert.proposals().iter()) + .filter(|&(a, b)| a.external_id == b.external_id().to_string()) + .count(); + assert_eq!(explorer_cert.proposals.len(), matching_proposal); + } + + fn assert_vote_cast( + fragment_cert: Transaction, + explorer_cert: TransactionByIdCertificatesTransactionCertificateOnVoteCast, + ) { + let vote_cast_cert = fragment_cert.as_slice().payload().into_payload(); + + assert_eq!( + explorer_cert.vote_plan, + vote_cast_cert.vote_plan().to_string() + ); + assert_eq!( + explorer_cert.proposal_index as u8, + vote_cast_cert.proposal_index() + ); + } + + fn assert_vote_tally( + fragment_cert: Transaction, + explorer_cert: TransactionByIdCertificatesTransactionCertificateOnVoteTally, + ) { + let vote_tally_cert = fragment_cert.as_slice().payload().into_payload(); + assert_eq!(explorer_cert.vote_plan, vote_tally_cert.id().to_string()); + } + + fn assert_update_proposal( + fragment_cert: Transaction, + explorer_cert: TransactionByIdCertificatesTransactionCertificateOnUpdateProposal, + ) { + let update_proposal_cert = fragment_cert.as_slice().payload().into_payload(); + assert_eq!( + Self::decode_bech32_pk(&explorer_cert.proposer_id.id), + *update_proposal_cert.proposer_id().as_public_key() + ); + assert_eq!( + explorer_cert.changes.config_params.len(), + update_proposal_cert.changes().iter().len() + ); + + //for each parameter in the update_proposal_certificate check that there is only one parameter + //of the corrisponding type in the explorer query answer and that the parameters have the same value + for update_proposal_param in update_proposal_cert.changes().iter() { + match update_proposal_param { + Block0Date(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::Block0Date(explorer_param) + if explorer_param.block0_date as u64 == certificate_param.0) + }) + .count(); + assert_eq!(matching_params, 1); + } + Discrimination(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::Discrimination(explorer_param) + if { match explorer_param.discrimination{ + DiscriminationEnum::PRODUCTION => {matches!(certificate_param, chain_addr::Discrimination::Production)}, + DiscriminationEnum::TEST => {matches!(certificate_param, chain_addr::Discrimination::Test)}, + DiscriminationEnum::Other(_) => false, + }})).count(); + assert_eq!(matching_params, 1); + } + ConsensusVersion(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::ConsensusType(explorer_param) + if { match explorer_param.consensus_type{ + ConsensusTypeEnum::BFT => {matches!(certificate_param, ConsensusType::Bft)}, + ConsensusTypeEnum::GENESIS_PRAOS => {matches!(certificate_param, ConsensusType::GenesisPraos)}, + ConsensusTypeEnum::Other(_) => false, + }})).count(); + assert_eq!(matching_params, 1); + } + SlotsPerEpoch(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::SlotsPerEpoch(explorer_param) + if explorer_param.slots_per_epoch as u32 == *certificate_param) + }) + .count(); + assert_eq!(matching_params, 1); + } + SlotDuration(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::SlotDuration(explorer_param) + if explorer_param.slot_duration as u8 == *certificate_param) + }) + .count(); + assert_eq!(matching_params, 1); + } + EpochStabilityDepth(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::EpochStabilityDepth(explorer_param) + if explorer_param.epoch_stability_depth as u32 == *certificate_param) + }) + .count(); + assert_eq!(matching_params, 1); + } + ConsensusGenesisPraosActiveSlotsCoeff(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::Milli(explorer_param) + if explorer_param.milli as u64 == certificate_param.to_millis()) + }) + .count(); + assert_eq!(matching_params, 1); + } + BlockContentMaxSize(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::BlockContentMaxSize(explorer_param) + if explorer_param.block_content_max_size as u32 == *certificate_param) + }) + .count(); + assert_eq!(matching_params, 1); + } + AddBftLeader(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::AddBftLeader(explorer_param) + if explorer_param.add_bft_leader.id == certificate_param.as_public_key().to_string())).count(); + assert_eq!(matching_params, 1); + } + RemoveBftLeader(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::RemoveBftLeader(explorer_param) + if explorer_param.remove_bft_leader.id == certificate_param.as_public_key().to_string())).count(); + assert_eq!(matching_params, 1); + } + LinearFee(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::LinearFee(explorer_param) + if {explorer_param.certificate as u64 == certificate_param.certificate && + explorer_param.coefficient as u64 == certificate_param.coefficient && + explorer_param.constant as u64 == certificate_param.constant && + explorer_param.per_certificate_fees.certificate_owner_stake_delegation.unwrap() as u64 == u64::from(certificate_param.per_certificate_fees.certificate_owner_stake_delegation.unwrap()) && + explorer_param.per_certificate_fees.certificate_pool_registration.unwrap() as u64 == u64::from(certificate_param.per_certificate_fees.certificate_pool_registration.unwrap()) && + explorer_param.per_certificate_fees.certificate_stake_delegation.unwrap() as u64 == u64::from(certificate_param.per_certificate_fees.certificate_stake_delegation.unwrap()) && + explorer_param.per_vote_certificate_fees.certificate_vote_cast.unwrap() as u64 == u64::from(certificate_param.per_vote_certificate_fees.certificate_vote_cast.unwrap()) && + explorer_param.per_vote_certificate_fees.certificate_vote_plan.unwrap() as u64 == u64::from(certificate_param.per_vote_certificate_fees.certificate_vote_plan.unwrap()) + })).count(); + assert_eq!(matching_params, 1); + } + ProposalExpiration(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::ProposalExpiration(explorer_param) + if explorer_param.proposal_expiration as u32 == *certificate_param) + }) + .count(); + assert_eq!(matching_params, 1); + } + KesUpdateSpeed(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::KesUpdateSpeed(explorer_param) + if explorer_param.kes_update_speed as u32 == *certificate_param) + }) + .count(); + assert_eq!(matching_params, 1); + } + TreasuryAdd(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::TreasuryAdd(explorer_param) + if explorer_param.treasury_add == certificate_param.to_string()) + }) + .count(); + assert_eq!(matching_params, 1); + } + TreasuryParams(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::TreasuryParams(explorer_param) + if {explorer_param.treasury_params.fixed == certificate_param.fixed.to_string() && + explorer_param.treasury_params.ratio.numerator.parse::().unwrap() == certificate_param.ratio.numerator && + explorer_param.treasury_params.ratio.denominator.parse::().unwrap() == u64::from(certificate_param.ratio.denominator) && + explorer_param.treasury_params.max_limit.as_ref().unwrap().parse::().unwrap() == u64::from(certificate_param.max_limit.unwrap())} + )).count(); + assert_eq!(matching_params, 1); + } + RewardPot(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::RewardPot(explorer_param) + if explorer_param.reward_pot == certificate_param.to_string()) + }) + .count(); + assert_eq!(matching_params, 1); + } + RewardParams(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::RewardParams(explorer_param) + if { match &explorer_param.reward_params { + ConfigParamOnRewardParamsRewardParams::LinearRewardParams(exp_linear_param) => + {matches!(certificate_param, RewardParams::Linear { constant,ratio,epoch_rate,epoch_start } + if {*constant == exp_linear_param.constant as u64 && + ratio.numerator == exp_linear_param.ratio.numerator.parse::().unwrap() && + u64::from(ratio.denominator) == exp_linear_param.ratio.denominator.parse::().unwrap() && + u32::from(*epoch_rate) == exp_linear_param.epoch_rate as u32 && + *epoch_start == exp_linear_param.epoch_start as u32}) }, + ConfigParamOnRewardParamsRewardParams::HalvingRewardParams(exp_halving_param) => + {matches!(certificate_param, RewardParams::Halving { constant,ratio,epoch_rate,epoch_start } + if {*constant == exp_halving_param.constant as u64 && + ratio.numerator == exp_halving_param.ratio.numerator.parse::().unwrap() && + u64::from(ratio.denominator) == exp_halving_param.ratio.denominator.parse::().unwrap() && + u32::from(*epoch_rate) == exp_halving_param.epoch_rate as u32 && + *epoch_start == exp_halving_param.epoch_start as u32}) }, + }})).count(); + assert_eq!(matching_params, 1); + } + PerCertificateFees(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::PerCertificateFee(explorer_param) + if { + explorer_param.certificate_owner_stake_delegation.unwrap() as u64 == u64::from(certificate_param.certificate_owner_stake_delegation.unwrap()) && + explorer_param.certificate_pool_registration.unwrap() as u64 == u64::from(certificate_param.certificate_pool_registration.unwrap()) && + explorer_param.certificate_stake_delegation.unwrap() as u64 == u64::from(certificate_param.certificate_stake_delegation.unwrap()) + })).count(); + assert_eq!(matching_params, 1); + } + FeesInTreasury(certificate_param) => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::FeesInTreasury(explorer_param) + if explorer_param.fees_in_treasury == *certificate_param) + }) + .count(); + assert_eq!(matching_params, 1); + } + RewardLimitNone => { + let matching_params = explorer_cert + .changes + .config_params + .iter() + .filter(|&config_param| { + matches!(config_param, configParam::RewardLimitNone(explorer_param) + if !explorer_param.reward_limit_none) + }) + .count(); + assert_eq!(matching_params, 1); + } + RewardLimitByAbsoluteStake(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::RewardLimitByAbsoluteStake(explorer_param) + if explorer_param.reward_limit_by_absolute_stake.numerator.parse::().unwrap() == certificate_param.numerator && + explorer_param.reward_limit_by_absolute_stake.denominator.parse::().unwrap() == u64::from(certificate_param.denominator))).count(); + assert_eq!(matching_params, 1); + } + PoolRewardParticipationCapping(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::PoolRewardParticipationCapping(explorer_param) + if explorer_param.max as u32 == u32::from(certificate_param.0) && + explorer_param.min as u32 == u32::from(certificate_param.1))).count(); + assert_eq!(matching_params, 1); + } + AddCommitteeId(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::AddCommitteeId(explorer_param) + if explorer_param.add_committee_id == certificate_param.public_key().to_string())).count(); + assert_eq!(matching_params, 1); + } + RemoveCommitteeId(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::RemoveCommitteeId(explorer_param) + if explorer_param.remove_committee_id == certificate_param.public_key().to_string())).count(); + assert_eq!(matching_params, 1); + } + PerVoteCertificateFees(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::PerVoteCertificateFee(explorer_param) + if {explorer_param.certificate_vote_cast.unwrap() as u64 == u64::from(certificate_param.certificate_vote_cast.unwrap()) && + explorer_param.certificate_vote_plan.unwrap() as u64 == u64::from(certificate_param.certificate_vote_plan.unwrap()) + })).count(); + assert_eq!(matching_params, 1); + } + TransactionMaxExpiryEpochs(certificate_param) => { + let matching_params = explorer_cert.changes.config_params.iter() + .filter(|&config_param| matches!(config_param, configParam::TransactionMaxExpiryEpochs(explorer_param) + if explorer_param.transaction_max_expiry_epochs as u8 == *certificate_param)).count(); + assert_eq!(matching_params, 1); + } + #[cfg(feature = "evm")] + EvmConfiguration(_) => unimplemented!(), + #[cfg(feature = "evm")] + EvmEnvironment(_) => unimplemented!(), + } + } + } + + fn assert_update_vote( + fragment_cert: Transaction, + explorer_cert: TransactionByIdCertificatesTransactionCertificateOnUpdateVote, + ) { + let update_vote_cert = fragment_cert.as_slice().payload().into_payload(); + assert_eq!( + explorer_cert.proposal_id, + update_vote_cert.proposal_id().to_string() + ); + assert_eq!( + Self::decode_bech32_pk(&explorer_cert.voter_id.id), + *update_vote_cert.voter_id().as_public_key() + ); + } + pub fn assert_epoch_stability_depth(depth: u32, explorer_depth: i64) { assert_eq!(depth as u64, explorer_depth as u64); } diff --git a/testing/jormungandr-integration-tests/src/jormungandr/explorer/certificates.rs b/testing/jormungandr-integration-tests/src/jormungandr/explorer/certificates.rs index 1310fc4c2f..a4cd1c7049 100644 --- a/testing/jormungandr-integration-tests/src/jormungandr/explorer/certificates.rs +++ b/testing/jormungandr-integration-tests/src/jormungandr/explorer/certificates.rs @@ -1,19 +1,38 @@ +use crate::startup; use assert_fs::TempDir; +use chain_addr::Discrimination; +use chain_core::property::BlockDate as propertyBlockDate; +use chain_crypto::Ed25519; #[cfg(feature = "evm")] use chain_impl_mockchain::testing::TestGen; -use chain_impl_mockchain::{block::BlockDate, transaction::AccountIdentifier}; +use chain_impl_mockchain::{ + block::BlockDate, + certificate::{UpdateProposal, UpdateVote, VoteAction, VoteTallyPayload}, + fee::LinearFee, + tokens::minting_policy::MintingPolicy, + transaction::AccountIdentifier, + vote::Choice, +}; use jormungandr_automation::{ jcli::JCli, jormungandr::{ explorer::{configuration::ExplorerParams, verifier::ExplorerVerifier}, ConfigurationBuilder, Starter, }, + testing::{ + keys::create_new_key_pair, + time::{wait_for_date, wait_for_epoch}, + VotePlanBuilder, + }, +}; +use jormungandr_lib::interfaces::{BlockContentMaxSize, ConfigParam, ConfigParams, InitialToken}; +use thor::{ + BlockDateGenerator::Fixed, FragmentBuilder, FragmentSender, StakePool, TransactionHash, }; -use thor::{FragmentBuilder, FragmentSender, StakePool, TransactionHash}; #[test] pub fn explorer_stake_pool_registration_test() { - let query_complexity_limit = 70; + let query_complexity_limit = 140; let query_depth_limit = 30; let temp_dir = TempDir::new().unwrap(); let mut first_stake_pool_owner = thor::Wallet::default(); @@ -77,7 +96,7 @@ pub fn explorer_stake_pool_registration_test() { #[test] pub fn explorer_owner_delegation_test() { - let query_complexity_limit = 70; + let query_complexity_limit = 140; let query_depth_limit = 30; let temp_dir = TempDir::new().unwrap(); let mut stake_pool_owner = thor::Wallet::default(); @@ -148,7 +167,7 @@ pub fn explorer_owner_delegation_test() { #[test] pub fn explorer_full_delegation_test() { - let query_complexity_limit = 70; + let query_complexity_limit = 140; let query_depth_limit = 30; let temp_dir = TempDir::new().unwrap(); let mut stake_pool_owner = thor::Wallet::default(); @@ -224,7 +243,7 @@ pub fn explorer_full_delegation_test() { #[test] pub fn explorer_split_delegation_test() { - let query_complexity_limit = 70; + let query_complexity_limit = 140; let query_depth_limit = 30; let temp_dir = TempDir::new().unwrap(); let mut first_stake_pool_owner = thor::Wallet::default(); @@ -321,7 +340,7 @@ pub fn explorer_split_delegation_test() { #[test] pub fn explorer_pool_update_test() { - let query_complexity_limit = 70; + let query_complexity_limit = 140; let query_depth_limit = 30; let jcli: JCli = Default::default(); let temp_dir = TempDir::new().unwrap(); @@ -409,7 +428,7 @@ pub fn explorer_pool_update_test() { #[test] pub fn explorer_pool_retire_test() { - let query_complexity_limit = 70; + let query_complexity_limit = 140; let query_depth_limit = 30; let temp_dir = TempDir::new().unwrap(); let mut first_stake_pool_owner = thor::Wallet::default(); @@ -551,3 +570,411 @@ pub fn explorer_evm_mapping_certificates_test() { let _evm_mapping_transaction = trans.data.unwrap().transaction; } + +#[test] +pub fn explorer_vote_plan_certificates_test() { + let query_complexity_limit = 140; + let query_depth_limit = 30; + let mut first_stake_pool_owner = thor::Wallet::default(); + let bob = thor::Wallet::default(); + let discrimination = Discrimination::Test; + + let vote_plan = VotePlanBuilder::new() + .proposals_count(3) + .action_type(VoteAction::OffChain) + .vote_start(propertyBlockDate::from_epoch_slot_id(1, 0)) + .tally_start(propertyBlockDate::from_epoch_slot_id(20, 0)) + .tally_end(propertyBlockDate::from_epoch_slot_id(30, 0)) + .public() + .build(); + + let jormungandr = startup::start_bft( + vec![&first_stake_pool_owner, &bob], + ConfigurationBuilder::new() + .with_discrimination(discrimination) + .with_slots_per_epoch(20) + .with_slot_duration(3) + .with_linear_fees(LinearFee::new(0, 0, 0)), + ) + .unwrap(); + + let fragment_sender = FragmentSender::new( + jormungandr.genesis_block_hash(), + jormungandr.fees(), + BlockDate::first().next_epoch().into(), + Default::default(), + ); + + let fragment_builder = FragmentBuilder::new( + &jormungandr.genesis_block_hash(), + &jormungandr.fees(), + BlockDate::first().next_epoch(), + ); + + let params = ExplorerParams::new( + query_complexity_limit.to_string(), + query_depth_limit.to_string(), + None, + ); + let explorer_process = jormungandr.explorer(params); + let explorer = explorer_process.client(); + + let vote_plan_fragment = fragment_builder.vote_plan(&first_stake_pool_owner, &vote_plan); + + fragment_sender + .send_fragment( + &mut first_stake_pool_owner, + vote_plan_fragment.clone(), + &jormungandr, + ) + .unwrap(); + + let trans = explorer + .transaction_certificates(vote_plan_fragment.hash().into()) + .expect("vote plan transaction not found"); + + assert!(trans.errors.is_none(), "{:?}", trans.errors.unwrap()); + + let vote_plan_transaction = trans.data.unwrap().transaction; + + ExplorerVerifier::assert_transaction_certificates(vote_plan_fragment, vote_plan_transaction) + .unwrap(); +} + +#[test] +pub fn explorer_vote_cast_certificates_test() { + let query_complexity_limit = 140; + let query_depth_limit = 30; + let temp_dir = TempDir::new().unwrap(); + let mut alice = thor::Wallet::default(); + + let vote_plan = VotePlanBuilder::new() + .proposals_count(3) + .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(2, 0)) + .public() + .build(); + + let vote_plan_cert = thor::vote_plan_cert( + &alice, + chain_impl_mockchain::block::BlockDate { + epoch: 1, + slot_id: 0, + }, + &vote_plan, + ) + .into(); + let wallets = [&alice]; + let config = ConfigurationBuilder::new() + .with_funds(wallets.iter().map(|x| x.to_initial_fund(1000)).collect()) + .with_token(InitialToken { + token_id: vote_plan.voting_token().clone().into(), + policy: MintingPolicy::new().into(), + to: vec![alice.to_initial_token(1000)], + }) + .with_committees(&[alice.to_committee_id()]) + .with_slots_per_epoch(60) + .with_certs(vec![vote_plan_cert]) + .with_treasury(1_000.into()) + .build(&temp_dir); + + let jormungandr = Starter::new() + .config(config) + .temp_dir(temp_dir) + .start() + .unwrap(); + + let fragment_sender = FragmentSender::new( + jormungandr.genesis_block_hash(), + jormungandr.fees(), + BlockDate::first().next_epoch().into(), + Default::default(), + ); + + let fragment_builder = FragmentBuilder::new( + &jormungandr.genesis_block_hash(), + &jormungandr.fees(), + BlockDate::first().next_epoch(), + ); + + let params = ExplorerParams::new( + query_complexity_limit.to_string(), + query_depth_limit.to_string(), + None, + ); + let explorer_process = jormungandr.explorer(params); + let explorer = explorer_process.client(); + + let vote_cast_fragment = fragment_builder.vote_cast(&alice, &vote_plan, 2, &Choice::new(0)); + + fragment_sender + .send_fragment(&mut alice, vote_cast_fragment.clone(), &jormungandr) + .unwrap(); + + let trans = explorer + .transaction_certificates(vote_cast_fragment.hash().into()) + .expect("vote cast transaction not found"); + + assert!(trans.errors.is_none(), "{:?}", trans.errors.unwrap()); + + let vote_cast_transaction = trans.data.unwrap().transaction; + ExplorerVerifier::assert_transaction_certificates(vote_cast_fragment, vote_cast_transaction) + .unwrap(); +} + +#[test] +pub fn explorer_vote_tally_certificate_test() { + let query_complexity_limit = 140; + let query_depth_limit = 30; + let temp_dir = TempDir::new().unwrap(); + let mut alice = thor::Wallet::default(); + + let vote_plan = VotePlanBuilder::new() + .proposals_count(3) + .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(2, 0)) + .public() + .build(); + + let vote_plan_cert = thor::vote_plan_cert( + &alice, + chain_impl_mockchain::block::BlockDate { + epoch: 1, + slot_id: 0, + }, + &vote_plan, + ) + .into(); + let wallets = [&alice]; + let config = ConfigurationBuilder::new() + .with_funds(wallets.iter().map(|x| x.to_initial_fund(1000)).collect()) + .with_token(InitialToken { + token_id: vote_plan.voting_token().clone().into(), + policy: MintingPolicy::new().into(), + to: vec![alice.to_initial_token(1000)], + }) + .with_committees(&[alice.to_committee_id()]) + .with_slots_per_epoch(60) + .with_certs(vec![vote_plan_cert]) + .with_treasury(1_000.into()) + .build(&temp_dir); + + let jormungandr = Starter::new() + .config(config) + .temp_dir(temp_dir) + .start() + .unwrap(); + + let fragment_sender = FragmentSender::new( + jormungandr.genesis_block_hash(), + jormungandr.fees(), + Fixed(BlockDate { + epoch: 2, + slot_id: 0, + }), + Default::default(), + ); + + let fragment_builder = FragmentBuilder::new( + &jormungandr.genesis_block_hash(), + &jormungandr.fees(), + BlockDate { + epoch: 2, + slot_id: 0, + }, + ); + + let params = ExplorerParams::new( + query_complexity_limit.to_string(), + query_depth_limit.to_string(), + None, + ); + let explorer_process = jormungandr.explorer(params); + let explorer = explorer_process.client(); + + let vote_cast_fragment = fragment_builder.vote_cast(&alice, &vote_plan, 2, &Choice::new(0)); + + fragment_sender + .send_fragment(&mut alice, vote_cast_fragment, &jormungandr) + .unwrap(); + + wait_for_epoch(1, jormungandr.rest()); + + let vote_tally_fragment = + fragment_builder.vote_tally(&alice, &vote_plan, VoteTallyPayload::Public); + + fragment_sender + .send_fragment(&mut alice, vote_tally_fragment.clone(), &jormungandr) + .unwrap(); + + let trans = explorer + .transaction_certificates(vote_tally_fragment.hash().into()) + .expect("vote tally transaction not found"); + + assert!(trans.errors.is_none(), "{:?}", trans.errors.unwrap()); + + let vote_tally_transaction = trans.data.unwrap().transaction; + ExplorerVerifier::assert_transaction_certificates(vote_tally_fragment, vote_tally_transaction) + .unwrap(); +} + +#[should_panic] //bug NPG-2742 +#[test] +pub fn explorer_update_proposal_certificate_test() { + let query_complexity_limit = 140; + let query_depth_limit = 30; + let temp_dir = TempDir::new().unwrap(); + let mut alice = thor::Wallet::default(); + let mut bob = thor::Wallet::default(); + let bft_secret_alice = create_new_key_pair::(); + let bft_secret_bob = create_new_key_pair::(); + let wallet_initial_funds = 5_000_000; + + let config = ConfigurationBuilder::new() + .with_funds(vec![ + alice.to_initial_fund(wallet_initial_funds), + bob.to_initial_fund(wallet_initial_funds), + ]) + .with_consensus_leaders_ids(vec![ + bft_secret_alice.identifier().into(), + bft_secret_bob.identifier().into(), + ]) + .with_proposal_expiry_epochs(20) + .with_slots_per_epoch(20) + .with_linear_fees(LinearFee::new(0, 0, 0)) + .build(&temp_dir); + + let jormungandr = Starter::new() + .temp_dir(temp_dir) + .config(config) + .start() + .unwrap(); + + let new_block_context_max_size = 1000; + let change_params = ConfigParams::new(vec![ConfigParam::BlockContentMaxSize( + BlockContentMaxSize::from(new_block_context_max_size), + )]); + + let update_proposal = UpdateProposal::new( + change_params.into(), + bft_secret_alice.identifier().into_public_key().into(), + ); + + let fragment_sender = FragmentSender::new( + jormungandr.genesis_block_hash(), + jormungandr.fees(), + Fixed(BlockDate { + epoch: 10, + slot_id: 0, + }), + Default::default(), + ); + + let fragment_builder = FragmentBuilder::new( + &jormungandr.genesis_block_hash(), + &jormungandr.fees(), + BlockDate { + epoch: 10, + slot_id: 0, + }, + ); + + let params = ExplorerParams::new( + query_complexity_limit.to_string(), + query_depth_limit.to_string(), + None, + ); + let explorer_process = jormungandr.explorer(params); + let explorer = explorer_process.client(); + + let proposal_update_fragment = fragment_builder.update_proposal( + &alice, + update_proposal, + &bft_secret_alice.signing_key().into_secret_key(), + ); + + fragment_sender + .send_fragment(&mut alice, proposal_update_fragment.clone(), &jormungandr) + .unwrap(); + + wait_for_date( + BlockDate { + epoch: 0, + slot_id: 10, + } + .into(), + jormungandr.rest(), + ); + + let update_vote = UpdateVote::new( + proposal_update_fragment.hash(), + bft_secret_alice.identifier().into_public_key().into(), + ); + + fragment_sender + .send_update_vote( + &mut alice, + &bft_secret_alice.signing_key().into_secret_key(), + update_vote, + &jormungandr, + ) + .unwrap(); + + let update_vote = UpdateVote::new( + proposal_update_fragment.hash(), + bft_secret_bob.identifier().into_public_key().into(), + ); + + let update_vote_fragment = fragment_builder.update_vote( + &bob, + update_vote, + &bft_secret_bob.signing_key().into_secret_key(), + ); + + fragment_sender + .send_fragment(&mut bob, update_vote_fragment.clone(), &jormungandr) + .unwrap(); + + wait_for_date( + BlockDate { + epoch: 1, + slot_id: 0, + } + .into(), + jormungandr.rest(), + ); + + let update_vote_resp = explorer + .transaction_certificates(update_vote_fragment.hash().into()) + .expect("update vote transaction not found"); + + assert!( + update_vote_resp.errors.is_none(), + "{:?}", + update_vote_resp.errors.unwrap() + ); + let update_vote_transaction = update_vote_resp.data.unwrap().transaction; + ExplorerVerifier::assert_transaction_certificates( + update_vote_fragment, + update_vote_transaction, + ) + .unwrap(); + + let proposal_update_resp = explorer + .transaction_certificates(proposal_update_fragment.hash().into()) + .expect("update proposal transaction not found"); + + assert!( + proposal_update_resp.errors.is_none(), + "{:?}", + proposal_update_resp.errors.unwrap() + ); + let proposal_update_transaction = proposal_update_resp.data.unwrap().transaction; + ExplorerVerifier::assert_transaction_certificates( + proposal_update_fragment, + proposal_update_transaction, + ) + .unwrap(); +} diff --git a/testing/jormungandr-integration-tests/src/jormungandr/explorer/transaction.rs b/testing/jormungandr-integration-tests/src/jormungandr/explorer/transaction.rs index ce7027a3e6..e14a39c3ee 100644 --- a/testing/jormungandr-integration-tests/src/jormungandr/explorer/transaction.rs +++ b/testing/jormungandr-integration-tests/src/jormungandr/explorer/transaction.rs @@ -18,7 +18,7 @@ pub fn explorer_transaction_test() { let sender = thor::Wallet::default(); let receiver = thor::Wallet::default(); let transaction_value = 1_000; - let query_complexity_limit = 70; + let query_complexity_limit = 140; let attempts_number = 20; let mut config = ConfigurationBuilder::new();