diff --git a/Cargo.lock b/Cargo.lock index 00c0ccfa..ffadc777 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,7 +516,7 @@ dependencies = [ [[package]] name = "catalyst-toolbox" version = "0.5.0" -source = "git+https://github.com/input-output-hk/catalyst-toolbox.git?branch=catalyst-fund9#3ff3e5fed70f5a8adb400a6764945b250d1599e3" +source = "git+https://github.com/input-output-hk/catalyst-toolbox.git?branch=catalyst-fund9#9abbf7e3a259ca79433976859ad9b2e0d503b4e9" dependencies = [ "assert_fs", "bech32 0.8.1", @@ -2801,6 +2801,7 @@ dependencies = [ "jormungandr-lib", "jortestkit", "libmath", + "mainnet-tools", "rand 0.8.5", "rand_chacha 0.2.2", "registration-service", @@ -2814,6 +2815,7 @@ dependencies = [ "vit-servicing-station-lib", "vit-servicing-station-tests", "vitup", + "voting-hir", ] [[package]] @@ -3315,6 +3317,29 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "mainnet-tools" +version = "0.1.0" +dependencies = [ + "assert_fs", + "bech32 0.8.1", + "catalyst-toolbox", + "chain-addr", + "chain-crypto", + "futures", + "hex", + "jormungandr-lib", + "rand 0.8.5", + "serde", + "serde_json", + "signals-handler", + "structopt", + "tempdir", + "thiserror", + "thor", + "tokio 1.19.2", +] + [[package]] name = "matchers" version = "0.0.1" @@ -5552,11 +5577,13 @@ name = "snapshot-trigger-service" version = "0.1.0" dependencies = [ "assert_fs", + "catalyst-toolbox", "chain-addr", "chrono", "futures", "jormungandr-lib", "jortestkit", + "mainnet-tools", "reqwest 0.10.10", "serde", "serde_json", @@ -6825,7 +6852,7 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "voting-hir" version = "0.1.0" -source = "git+https://github.com/input-output-hk/catalyst-toolbox.git?branch=catalyst-fund9#3ff3e5fed70f5a8adb400a6764945b250d1599e3" +source = "git+https://github.com/input-output-hk/catalyst-toolbox.git?branch=catalyst-fund9#9abbf7e3a259ca79433976859ad9b2e0d503b4e9" dependencies = [ "jormungandr-lib", "serde", diff --git a/Cargo.toml b/Cargo.toml index bb084dec..031cc5da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,9 @@ members = [ "vitup", "valgrind", "integration-tests", + "mainnet-tools", "registration-service", "registration-verify-service", "snapshot-trigger-service", - "signals-handler", + "signals-handler" ] diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 56cdc200..eb7b1343 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -22,12 +22,14 @@ chain-crypto = { git = "https://github.com/input-output-hk/chain-libs.git", bran vitup = { path = "../vitup"} iapyx = { path = "../iapyx"} valgrind = { path = "../valgrind"} +mainnet-tools = { path = "../mainnet-tools"} snapshot-trigger-service = { path = "../snapshot-trigger-service" } registration-service = { path = "../registration-service" } rand_chacha = "0.2" tokio = { version = "1.1", features = ["macros","rt","rt-multi-thread"] } chain-impl-mockchain = { git = "https://github.com/input-output-hk/chain-libs.git", branch = "catalyst-fund9", features = [ "property-test-api" ] } catalyst-toolbox = { git = "https://github.com/input-output-hk/catalyst-toolbox.git", branch = "catalyst-fund9", features=["test-api"]} +voting-hir = { git = "https://github.com/input-output-hk/catalyst-toolbox.git", branch = "catalyst-fund9"} thiserror = "1.0" rand = "0.8" libmath = "0.2.1" diff --git a/integration-tests/src/backend/rewards/mod.rs b/integration-tests/src/backend/rewards/mod.rs deleted file mode 100644 index 924822a7..00000000 --- a/integration-tests/src/backend/rewards/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -//mod active_voters_rewards; -mod proposers_fund; diff --git a/integration-tests/src/common/mainnet_wallet_ext.rs b/integration-tests/src/common/mainnet_wallet_ext.rs new file mode 100644 index 00000000..96ea6d51 --- /dev/null +++ b/integration-tests/src/common/mainnet_wallet_ext.rs @@ -0,0 +1,16 @@ +use crate::common::MainnetWallet; +use vitup::config::Block0Initial; + +pub trait MainnetWalletExtension { + fn as_initial_entry(&self) -> Block0Initial; +} + +impl MainnetWalletExtension for MainnetWallet { + fn as_initial_entry(&self) -> Block0Initial { + Block0Initial::External { + address: self.catalyst_address().to_string(), + funds: self.stake(), + role: Default::default(), + } + } +} diff --git a/integration-tests/src/common/mod.rs b/integration-tests/src/common/mod.rs index 6c796c33..61cfccc7 100644 --- a/integration-tests/src/common/mod.rs +++ b/integration-tests/src/common/mod.rs @@ -1,6 +1,6 @@ mod assert; pub mod load; -mod mainnet; +pub mod mainnet_wallet_ext; pub mod registration; mod rewards; pub mod snapshot; @@ -9,7 +9,7 @@ mod vote_plan_status; mod wallet; pub use assert::*; -pub use mainnet::MainnetWallet; +pub use mainnet_tools::wallet::MainnetWallet; pub use rewards::{funded_proposals, VotesRegistry}; pub use static_data::SnapshotExtensions; use thiserror::Error; diff --git a/integration-tests/src/common/snapshot/controller.rs b/integration-tests/src/common/snapshot/controller.rs new file mode 100644 index 00000000..e12570bb --- /dev/null +++ b/integration-tests/src/common/snapshot/controller.rs @@ -0,0 +1,76 @@ +use catalyst_toolbox::snapshot::RawSnapshot; +use catalyst_toolbox::snapshot::Snapshot; +use catalyst_toolbox::snapshot::VotingRegistration; +use fraction::Fraction; +use jortestkit::prelude::Wait; +use snapshot_trigger_service::client::rest::SnapshotRestClient; +use catalyst_toolbox::snapshot::voting_group::VotingGroupAssigner; +use snapshot_trigger_service::config::Configuration; +use snapshot_trigger_service::config::JobParameters; +use std::process::Child; + +pub struct SnapshotServiceController { + child: Child, + configuration: Configuration, + client: SnapshotRestClient, +} + +impl SnapshotServiceController { + pub fn new(child: Child, configuration: Configuration) -> Self { + Self { + child, + client: SnapshotRestClient::new(format!("http://localhost:{}", configuration.port)), + configuration, + } + } + + pub fn client(&self) -> &SnapshotRestClient { + &self.client + } + + pub fn shutdown(mut self) -> Result<(), std::io::Error> { + self.child.kill() + } + + pub fn configuration(&self) -> &Configuration { + &self.configuration + } + + pub fn snapshot( + &self, + job_params: JobParameters, + threshold: u64, + fraction: Fraction, + voting_group_assigner: &impl VotingGroupAssigner, + ) -> Snapshot { + let id = self.client().job_new(job_params.clone()).unwrap(); + + self.client() + .wait_for_job_finish(&id, Wait::new(std::time::Duration::from_secs(10), 5)) + .unwrap(); + + let snapshot_content = self + .client() + .get_snapshot(id, job_params.tag.as_ref().unwrap().to_string()) + .unwrap(); + let raw_snapshot: Vec = + serde_json::from_str(&snapshot_content).unwrap(); + + Snapshot::from_raw_snapshot( + RawSnapshot::from(raw_snapshot), + threshold.into(), + fraction, + voting_group_assigner, + ) + .unwrap() + } +} + +impl Drop for SnapshotServiceController { + fn drop(&mut self) { + // There's no kill like overkill + let _ = self.child.kill(); + // FIXME: These should be better done in a test harness + self.child.wait().unwrap(); + } +} diff --git a/integration-tests/src/common/snapshot.rs b/integration-tests/src/common/snapshot/mod.rs similarity index 55% rename from integration-tests/src/common/snapshot.rs rename to integration-tests/src/common/snapshot/mod.rs index cd18aebf..1dd268af 100644 --- a/integration-tests/src/common/snapshot.rs +++ b/integration-tests/src/common/snapshot/mod.rs @@ -1,8 +1,15 @@ +mod controller; +mod starter; + +pub use controller::SnapshotServiceController; +pub use starter::SnapshotServiceStarter; + use snapshot_trigger_service::client::do_snapshot as do_snapshot_internal; -use snapshot_trigger_service::client::{Error, SnapshotResult}; +use snapshot_trigger_service::client::{Error as SnapshotClientError, SnapshotResult}; use snapshot_trigger_service::config::JobParameters; +use thiserror::Error; -pub fn do_snapshot(job_params: JobParameters) -> Result { +pub fn do_snapshot(job_params: JobParameters) -> Result { let snapshot_token = std::env::var("SNAPSHOT_TOKEN").expect("SNAPSHOT_TOKEN not defined"); let snapshot_address = std::env::var("SNAPSHOT_ADDRESS").expect("SNAPSHOT_ADDRESS not defined"); @@ -14,3 +21,13 @@ pub fn wait_for_db_sync() { std::thread::sleep(std::time::Duration::from_secs(5 * 60)); println!("Wait finished."); } + +#[derive(Error, Debug)] +pub enum Error { + #[error("spawn error")] + Io(#[from] std::io::Error), + #[error(transparent)] + SnapshotClient(#[from] SnapshotClientError), + #[error(transparent)] + Config(#[from] snapshot_trigger_service::config::Error), +} diff --git a/integration-tests/src/common/snapshot/starter.rs b/integration-tests/src/common/snapshot/starter.rs new file mode 100644 index 00000000..4b5342e3 --- /dev/null +++ b/integration-tests/src/common/snapshot/starter.rs @@ -0,0 +1,48 @@ +use crate::common::snapshot::SnapshotServiceController; +use assert_fs::fixture::PathChild; +use assert_fs::TempDir; +use snapshot_trigger_service::config::write_config; +use snapshot_trigger_service::config::Configuration; +use std::path::Path; +use std::path::PathBuf; + +use super::Error; +use std::process::Command; + +pub struct SnapshotServiceStarter { + configuration: Configuration, + path_to_bin: PathBuf, +} + +impl Default for SnapshotServiceStarter { + fn default() -> Self { + Self { + configuration: Default::default(), + path_to_bin: Path::new("snapshot-trigger-service").to_path_buf(), + } + } +} + +impl SnapshotServiceStarter { + pub fn with_configuration(mut self, configuration: Configuration) -> Self { + self.configuration = configuration; + self + } + + pub fn with_path_to_bin>(mut self, path: P) -> Self { + self.path_to_bin = path.as_ref().to_path_buf(); + self + } + + pub fn start(self, temp_dir: &TempDir) -> Result { + let config_file = temp_dir.child("snapshot_trigger_service_config.yaml"); + write_config(self.configuration.clone(), config_file.path())?; + Ok(SnapshotServiceController::new( + Command::new(self.path_to_bin) + .arg("--config") + .arg(config_file.path()) + .spawn()?, + self.configuration, + )) + } +} diff --git a/integration-tests/src/backend/features/batch.rs b/integration-tests/src/component/backend/features/batch.rs similarity index 100% rename from integration-tests/src/backend/features/batch.rs rename to integration-tests/src/component/backend/features/batch.rs diff --git a/integration-tests/src/backend/features/mod.rs b/integration-tests/src/component/backend/features/mod.rs similarity index 100% rename from integration-tests/src/backend/features/mod.rs rename to integration-tests/src/component/backend/features/mod.rs diff --git a/integration-tests/src/backend/features/persistent_log.rs b/integration-tests/src/component/backend/features/persistent_log.rs similarity index 100% rename from integration-tests/src/backend/features/persistent_log.rs rename to integration-tests/src/component/backend/features/persistent_log.rs diff --git a/integration-tests/src/backend/features/update_proposal.rs b/integration-tests/src/component/backend/features/update_proposal.rs similarity index 100% rename from integration-tests/src/backend/features/update_proposal.rs rename to integration-tests/src/component/backend/features/update_proposal.rs diff --git a/integration-tests/src/backend/features/votes_history.rs b/integration-tests/src/component/backend/features/votes_history.rs similarity index 100% rename from integration-tests/src/backend/features/votes_history.rs rename to integration-tests/src/component/backend/features/votes_history.rs diff --git a/integration-tests/src/backend/mod.rs b/integration-tests/src/component/backend/mod.rs similarity index 100% rename from integration-tests/src/backend/mod.rs rename to integration-tests/src/component/backend/mod.rs diff --git a/integration-tests/src/component/backend/rewards/mod.rs b/integration-tests/src/component/backend/rewards/mod.rs new file mode 100644 index 00000000..41300e2f --- /dev/null +++ b/integration-tests/src/component/backend/rewards/mod.rs @@ -0,0 +1 @@ +mod proposers_fund; \ No newline at end of file diff --git a/integration-tests/src/backend/rewards/proposers_fund.rs b/integration-tests/src/component/backend/rewards/proposers_fund.rs similarity index 100% rename from integration-tests/src/backend/rewards/proposers_fund.rs rename to integration-tests/src/component/backend/rewards/proposers_fund.rs diff --git a/integration-tests/src/backend/sanity/mod.rs b/integration-tests/src/component/backend/sanity/mod.rs similarity index 100% rename from integration-tests/src/backend/sanity/mod.rs rename to integration-tests/src/component/backend/sanity/mod.rs diff --git a/integration-tests/src/backend/sanity/private.rs b/integration-tests/src/component/backend/sanity/private.rs similarity index 100% rename from integration-tests/src/backend/sanity/private.rs rename to integration-tests/src/component/backend/sanity/private.rs diff --git a/integration-tests/src/backend/sanity/public.rs b/integration-tests/src/component/backend/sanity/public.rs similarity index 100% rename from integration-tests/src/backend/sanity/public.rs rename to integration-tests/src/component/backend/sanity/public.rs diff --git a/integration-tests/src/component/mod.rs b/integration-tests/src/component/mod.rs new file mode 100644 index 00000000..e7ac1efd --- /dev/null +++ b/integration-tests/src/component/mod.rs @@ -0,0 +1,3 @@ +mod backend; +mod registration; +mod snapshot; \ No newline at end of file diff --git a/integration-tests/src/registration/mod.rs b/integration-tests/src/component/registration/mod.rs similarity index 100% rename from integration-tests/src/registration/mod.rs rename to integration-tests/src/component/registration/mod.rs diff --git a/integration-tests/src/registration/testnet.rs b/integration-tests/src/component/registration/testnet.rs similarity index 100% rename from integration-tests/src/registration/testnet.rs rename to integration-tests/src/component/registration/testnet.rs diff --git a/integration-tests/src/component/snapshot/local.rs b/integration-tests/src/component/snapshot/local.rs new file mode 100644 index 00000000..809612e5 --- /dev/null +++ b/integration-tests/src/component/snapshot/local.rs @@ -0,0 +1,40 @@ +use crate::common::registration::do_registration; +use crate::common::snapshot::do_snapshot; +use crate::common::snapshot::wait_for_db_sync; +use assert_fs::TempDir; +use jormungandr_automation::testing::asserts::InitialsAssert; +use snapshot_trigger_service::config::JobParameters; +const GRACE_PERIOD_FOR_SNAPSHOT: u64 = 300; + +#[test] +pub fn mixed_registration_transactions() { + let temp_dir = TempDir::new().unwrap().into_persistent(); + + let first_registartion = do_registration(&temp_dir); + first_registartion.assert_status_is_finished(); + first_registartion.assert_qr_equals_to_sk(); + + let overriden_entry = first_registartion.snapshot_entry().unwrap(); + + println!("Waiting 10 mins before running next registration"); + std::thread::sleep(std::time::Duration::from_secs(5 * 60)); + println!("Wait finished."); + + let second_registartion = do_registration(&temp_dir); + second_registartion.assert_status_is_finished(); + second_registartion.assert_qr_equals_to_sk(); + + let correct_entry = second_registartion.snapshot_entry().unwrap(); + + let job_param = JobParameters { + slot_no: Some(second_registartion.slot_no().unwrap() + GRACE_PERIOD_FOR_SNAPSHOT), + tag: None, + }; + + wait_for_db_sync(); + let snapshot_result = do_snapshot(job_param).unwrap(); + let initials = snapshot_result.initials(); + + initials.assert_contains(correct_entry); + initials.assert_not_contain(overriden_entry); +} \ No newline at end of file diff --git a/integration-tests/src/snapshot/mod.rs b/integration-tests/src/component/snapshot/mod.rs similarity index 81% rename from integration-tests/src/snapshot/mod.rs rename to integration-tests/src/component/snapshot/mod.rs index b1bbe91c..919d93e0 100644 --- a/integration-tests/src/snapshot/mod.rs +++ b/integration-tests/src/component/snapshot/mod.rs @@ -1,2 +1,4 @@ #[cfg(feature = "testnet-tests")] mod testnet; + +mod local; \ No newline at end of file diff --git a/integration-tests/src/snapshot/testnet.rs b/integration-tests/src/component/snapshot/testnet.rs similarity index 100% rename from integration-tests/src/snapshot/testnet.rs rename to integration-tests/src/component/snapshot/testnet.rs diff --git a/integration-tests/src/backend/rewards/active_voters_rewards.rs b/integration-tests/src/e2e/local/active_voters_rewards.rs similarity index 64% rename from integration-tests/src/backend/rewards/active_voters_rewards.rs rename to integration-tests/src/e2e/local/active_voters_rewards.rs index ac3673ba..4bebd29a 100644 --- a/integration-tests/src/backend/rewards/active_voters_rewards.rs +++ b/integration-tests/src/e2e/local/active_voters_rewards.rs @@ -1,43 +1,64 @@ use crate::Vote; use crate::common::iapyx_from_mainnet; +use crate::common::mainnet_wallet_ext::MainnetWalletExtension; +use crate::common::snapshot::SnapshotServiceStarter; use crate::common::MainnetWallet; use assert_fs::TempDir; use catalyst_toolbox::rewards::voters::calc_voter_rewards; -use catalyst_toolbox::snapshot::RawSnapshot; -use catalyst_toolbox::snapshot::Snapshot; use chain_impl_mockchain::block::BlockDate; +use fraction::Fraction; use jormungandr_automation::testing::time; +use mainnet_tools::db_sync::DbSyncInstance; +use mainnet_tools::network::MainnetNetwork; +use mainnet_tools::voting_tools::VotingToolsMock; +use snapshot_trigger_service::config::ConfigurationBuilder; +use snapshot_trigger_service::config::JobParameters; use vit_servicing_station_tests::common::data::ArbitraryValidVotingTemplateGenerator; -use catalyst_toolbox::snapshot::voting_group::RepsVotersAssigner; +use catalyst_toolbox::snapshot::snapshot_test_api::DummyAssigner; use vitup::config::VoteBlockchainTime; use vitup::config::{Block0Initials, ConfigBuilder}; use vitup::testing::spawn_network; use vitup::testing::vitup_setup; -use fraction::Fraction; #[test] pub fn voters_with_at_least_one_vote() { + let testing_directory = TempDir::new().unwrap().into_persistent(); + let stake = 10_000; let alice_wallet = MainnetWallet::new(stake); let bob_wallet = MainnetWallet::new(stake); let clarice_wallet = MainnetWallet::new(stake); - let raw_snapshot = vec![ - alice_wallet.as_voting_registration(), - bob_wallet.as_voting_registration(), - clarice_wallet.as_voting_registration(), - ]; + let mut mainnet_network = MainnetNetwork::default(); + let mut db_sync_instance = DbSyncInstance::default(); - let voters_assigner = RepsVotersAssigner::new( - VotingGroup, - VotingGroup, - "https://drep.io", - ); + mainnet_network.sync_with(&mut db_sync_instance); - let snapshot = Snapshot::from_raw_snapshot(RawSnapshot::from(raw_snapshot), 450.into(), Fraction::new(1u8, 2u8),&voters_assigner).unwrap(); - let testing_directory = TempDir::new().unwrap().into_persistent(); + alice_wallet + .send_direct_voting_registration() + .to(&mut mainnet_network); + bob_wallet + .send_direct_voting_registration() + .to(&mut mainnet_network); + clarice_wallet + .send_direct_voting_registration() + .to(&mut mainnet_network); + + let voting_tools = + VotingToolsMock::default().connect_to_db_sync(&db_sync_instance, &testing_directory); + + let configuration = ConfigurationBuilder::default() + .with_voting_tools_params(voting_tools.into()) + .with_tmp_result_dir(&testing_directory) + .build(); + + let snapshot = SnapshotServiceStarter::default() + .with_configuration(configuration) + .start(&testing_directory) + .unwrap() + .snapshot(JobParameters::fund("fund9"), 450u64, Fraction::from(1u64),&DummyAssigner); let vote_timing = VoteBlockchainTime { vote_start: 0, @@ -104,7 +125,7 @@ pub fn voters_with_at_least_one_vote() { assert_eq!( records .iter() - .find(|(x, _y)| **x == alice_wallet.reward_address()) + .find(|(x, _y)| **x == alice_wallet.reward_address_as_bech32()) .unwrap() .1, &50u32.into() @@ -113,7 +134,7 @@ pub fn voters_with_at_least_one_vote() { assert_eq!( records .iter() - .find(|(x, _y)| **x == bob_wallet.reward_address()) + .find(|(x, _y)| **x == bob_wallet.reward_address_as_bech32()) .unwrap() .1, &50u32.into() diff --git a/integration-tests/src/e2e/local/mod.rs b/integration-tests/src/e2e/local/mod.rs new file mode 100644 index 00000000..6ea8d2f0 --- /dev/null +++ b/integration-tests/src/e2e/local/mod.rs @@ -0,0 +1 @@ +mod active_voters_rewards; \ No newline at end of file diff --git a/integration-tests/src/e2e/mod.rs b/integration-tests/src/e2e/mod.rs index b1bbe91c..20611614 100644 --- a/integration-tests/src/e2e/mod.rs +++ b/integration-tests/src/e2e/mod.rs @@ -1,2 +1,4 @@ #[cfg(feature = "testnet-tests")] mod testnet; + +mod local; diff --git a/integration-tests/src/integration/from_snapshot_to_catalyst_toolbox.rs b/integration-tests/src/integration/from_snapshot_to_catalyst_toolbox.rs new file mode 100644 index 00000000..8e264d20 --- /dev/null +++ b/integration-tests/src/integration/from_snapshot_to_catalyst_toolbox.rs @@ -0,0 +1,102 @@ +use catalyst_toolbox::snapshot::voting_group::RepsVotersAssigner; +use catalyst_toolbox::snapshot::Delegations; +use crate::common::snapshot::SnapshotServiceStarter; +use crate::common::MainnetWallet; +use assert_fs::TempDir; +use fraction::Fraction; +use mainnet_tools::db_sync::DbSyncInstance; +use mainnet_tools::network::MainnetNetwork; +use std::collections::HashSet; +use mainnet_tools::voting_tools::VotingToolsMock; +use snapshot_trigger_service::config::ConfigurationBuilder; +use snapshot_trigger_service::config::JobParameters; +use voting_hir::VoterHIR; + + +const DIRECT_VOTING_GROUP: &str = "direct"; +const REP_VOTING_GROUP: &str = "rep"; + +#[test] +pub fn cip36_mixed_delegation() { + let testing_directory = TempDir::new().unwrap().into_persistent(); + + + let stake = 10_000; + + let alice_voter = MainnetWallet::new(stake); + let bob_voter = MainnetWallet::new(stake); + let clarice_voter = MainnetWallet::new(stake); + + let david_representative = MainnetWallet::new(500); + let edgar_representative = MainnetWallet::new(1_000); + let fred_representative = MainnetWallet::new(8_000); + + let mut reps = HashSet::new(); + reps.insert(edgar_representative.catalyst_public_key()); + reps.insert(david_representative.catalyst_public_key()); + reps.insert(fred_representative.catalyst_public_key()); + + let mut mainnet_network = MainnetNetwork::default(); + let mut db_sync_instance = DbSyncInstance::default(); + + mainnet_network.sync_with(&mut db_sync_instance); + + alice_voter + .send_direct_voting_registration() + .to(&mut mainnet_network); + bob_voter + .send_voting_registration(Delegations::New( + vec![(david_representative.catalyst_public_key(),1)] + )) + .to(&mut mainnet_network); + clarice_voter + .send_voting_registration(Delegations::New( + vec![ + (david_representative.catalyst_public_key(),1), + (edgar_representative.catalyst_public_key(),1) + ] + )) + .to(&mut mainnet_network); + + let voting_tools = + VotingToolsMock::default().connect_to_db_sync(&db_sync_instance, &testing_directory); + + let configuration = ConfigurationBuilder::default() + .with_voting_tools_params(voting_tools.into()) + .with_tmp_result_dir(&testing_directory) + .build(); + + + let assigner = RepsVotersAssigner::new_from_repsdb(DIRECT_VOTING_GROUP.to_string(), REP_VOTING_GROUP.to_string(), reps).unwrap(); + + let voter_hir = SnapshotServiceStarter::default() + .with_configuration(configuration) + .start(&testing_directory) + .unwrap() + .snapshot(JobParameters::fund("fund9"), 450u64, Fraction::from(1u64),&assigner) + .to_voter_hir(); + + assert_eq!(voter_hir.len(),3); + assert!(voter_hir.contains( + &VoterHIR { + voting_key: alice_voter.catalyst_public_key(), + voting_group: DIRECT_VOTING_GROUP.to_string(), + voting_power: stake.into(), + } + )); + assert!(voter_hir.contains( + &VoterHIR { + voting_key: david_representative.catalyst_public_key(), + voting_group: REP_VOTING_GROUP.to_string(), + voting_power: (stake + stake/2).into(), + } + )); + assert!(voter_hir.contains( + &VoterHIR { + voting_key: edgar_representative.catalyst_public_key(), + voting_group: REP_VOTING_GROUP.to_string(), + voting_power: (stake/2).into(), + } + )); + assert!(!voter_hir.iter().any(|x| x.voting_key == fred_representative.catalyst_public_key())); +} \ No newline at end of file diff --git a/integration-tests/src/integration/from_snapshot_to_vitup.rs b/integration-tests/src/integration/from_snapshot_to_vitup.rs new file mode 100644 index 00000000..a13a60cf --- /dev/null +++ b/integration-tests/src/integration/from_snapshot_to_vitup.rs @@ -0,0 +1,91 @@ +use catalyst_toolbox::snapshot::voting_group::RepsVotersAssigner; +use catalyst_toolbox::snapshot::Delegations; +use crate::common::snapshot::SnapshotServiceStarter; +use crate::common::MainnetWallet; +use assert_fs::TempDir; +use fraction::Fraction; +use mainnet_tools::db_sync::DbSyncInstance; +use mainnet_tools::network::MainnetNetwork; +use std::collections::HashSet; +use mainnet_tools::voting_tools::VotingToolsMock; +use snapshot_trigger_service::config::ConfigurationBuilder; +use jormungandr_automation::testing::block0::read_initials; +use snapshot_trigger_service::config::JobParameters; +use voting_hir::VoterHIR; +use vitup::config::ConfigBuilder; + +const DIRECT_VOTING_GROUP: &str = "direct"; +const REP_VOTING_GROUP: &str = "rep"; + +#[test] +pub fn cip36_mixed_delegation_should_appear_in_block0() { + let testing_directory = TempDir::new().unwrap().into_persistent(); + + + let stake = 10_000; + + let alice_voter = MainnetWallet::new(stake); + let bob_voter = MainnetWallet::new(stake); + let clarice_voter = MainnetWallet::new(stake); + + let david_representative = MainnetWallet::new(500); + let edgar_representative = MainnetWallet::new(1_000); + let fred_representative = MainnetWallet::new(8_000); + + let mut reps = HashSet::new(); + reps.insert(edgar_representative.catalyst_public_key()); + reps.insert(david_representative.catalyst_public_key()); + reps.insert(fred_representative.catalyst_public_key()); + + let mut mainnet_network = MainnetNetwork::default(); + let mut db_sync_instance = DbSyncInstance::default(); + + mainnet_network.sync_with(&mut db_sync_instance); + + alice_voter + .send_direct_voting_registration() + .to(&mut mainnet_network); + bob_voter + .send_voting_registration(Delegations::New( + vec![(david_representative.catalyst_public_key(),1)] + )) + .to(&mut mainnet_network); + clarice_voter + .send_voting_registration(Delegations::New( + vec![ + (david_representative.catalyst_public_key(),1), + (edgar_representative.catalyst_public_key(),1) + ] + )) + .to(&mut mainnet_network); + + let voting_tools = + VotingToolsMock::default().connect_to_db_sync(&db_sync_instance, &testing_directory); + + let configuration = ConfigurationBuilder::default() + .with_voting_tools_params(voting_tools.into()) + .with_tmp_result_dir(&testing_directory) + .build(); + + + let assigner = RepsVotersAssigner::new_from_repsdb(DIRECT_VOTING_GROUP.to_string(), REP_VOTING_GROUP.to_string(), reps).unwrap(); + + let voter_hir = SnapshotServiceStarter::default() + .with_configuration(configuration) + .start(&testing_directory) + .unwrap() + .snapshot(JobParameters::fund("fund9"), 450u64, Fraction::from(1u64),&assigner) + .to_voter_hir(); + + + let config = ConfigBuilder::default().build(); + config.initials.block0.extend_from_external(read_initials(voter_hir).unwrap()); + println!("{:?}",config); +} + +pub fn write_config>(config: Configuration, path: P) -> Result<(), Error> { + use std::io::Write; + let mut file = std::fs::File::create(&path)?; + file.write_all(serde_json::to_string(&config)?.as_bytes()) + .map_err(Into::into) +} \ No newline at end of file diff --git a/integration-tests/src/integration/mod.rs b/integration-tests/src/integration/mod.rs new file mode 100644 index 00000000..0107961a --- /dev/null +++ b/integration-tests/src/integration/mod.rs @@ -0,0 +1,2 @@ +mod from_snapshot_to_catalyst_toolbox; +//mod from_snapshot_to_vitup; \ No newline at end of file diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index 3f8f8ff4..58306fd6 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -1,11 +1,10 @@ cfg_if::cfg_if! { if #[cfg(test)] { pub mod common; - pub mod backend; + pub mod component; pub mod non_functional; - pub mod registration; + pub mod integration; pub mod e2e; - pub mod snapshot; } } diff --git a/registration-verify-service/src/job/mod.rs b/registration-verify-service/src/job/mod.rs index 258f442b..193156d6 100644 --- a/registration-verify-service/src/job/mod.rs +++ b/registration-verify-service/src/job/mod.rs @@ -204,6 +204,7 @@ impl RegistrationVerifyJob { get_snapshot_from_history_by_id( job_id, + &request.tag.clone().unwrap_or_else(|| "".to_string()), self.snapshot_token.to_string(), self.snapshot_address.to_string(), )? diff --git a/snapshot-trigger-service/Cargo.toml b/snapshot-trigger-service/Cargo.toml index d8ecc13d..8a034eda 100644 --- a/snapshot-trigger-service/Cargo.toml +++ b/snapshot-trigger-service/Cargo.toml @@ -14,6 +14,7 @@ structopt = "0.3" jortestkit = { git = "https://github.com/input-output-hk/jortestkit.git", branch= "master" } jormungandr-lib = { git = "https://github.com/input-output-hk/jormungandr.git", branch = "catalyst-fund9" } chain-addr = { git = "https://github.com/input-output-hk/chain-libs.git", branch = "catalyst-fund9" } +catalyst-toolbox = { git = "https://github.com/input-output-hk/catalyst-toolbox.git", branch = "catalyst-fund9", features=["test-api"]} futures = "0.3.8" assert_fs = "1.0" serde = { version = "1", features = ["derive"] } @@ -23,6 +24,7 @@ tokio = { version = "1.2", features = ["macros","rt","process"] } serde_json = "1.0" serde_yaml = "0.8" signals-handler = { path = "../signals-handler" } +mainnet-tools = { path = "../mainnet-tools"} [dependencies.reqwest] version = "0.10.10" diff --git a/snapshot-trigger-service/src/bin/voting-tools-mock.rs b/snapshot-trigger-service/src/bin/voting-tools-mock.rs deleted file mode 100644 index 1cdf8ba4..00000000 --- a/snapshot-trigger-service/src/bin/voting-tools-mock.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::path::Path; -use std::path::PathBuf; -use structopt::StructOpt; - -pub fn main() { - std::env::set_var("RUST_BACKTRACE", "full"); - VotingToolsCommand::from_args().exec() -} - -#[derive(StructOpt, Debug)] -pub struct VotingToolsCommand { - #[structopt(long = "mainnet")] - pub mainnet: bool, - - #[structopt(long = "testnet-magic")] - pub testnet_magic: Option, - - #[structopt(long = "db")] - pub db: String, - - #[structopt(long = "db-user")] - pub db_user: String, - - #[structopt(long = "db-host")] - pub db_host: PathBuf, - - #[structopt(long = "out-file")] - pub out_file: PathBuf, - - #[structopt(long = "scale")] - pub scale: u64, - - #[structopt(long = "slot-no")] - pub slot_no: Option, -} - -impl VotingToolsCommand { - pub fn exec(&self) { - println!("Params: {:?}", self); - println!("slepping 5 sec.."); - std::thread::sleep(std::time::Duration::from_secs(5)); - println!("saving {:?}", self.out_file); - let content = "[ \ - { \ - \"reward_address\": \"0xe1ffff2912572257b59dca84c965e4638a09f1524af7a15787eb0d8a46\", \ - \"stake_public_key\": \"0xe7d6616840734686855ec80ee9658f5ead9e29e494ec6889a5d1988b50eb8d0f\", \ - \"voting_power\": 177689370111, \ - \"voting_public_key\": \"0xc21ddb4abb04bd5ce21091eef1676e44889d806e6e1a6a9a7dc25c0eba54cc33\" \ - }, \ - { \ - \"reward_address\": \"0xe1fffc8bcb1578a15413bf11413639fa270a9ffa36d9a0c4d2c93536fe\", \ - \"stake_public_key\": \"0x2f9a90d87321a255efd038fea5df2a2349ea2c32fa584b73f2a46f655f235919\", \ - \"voting_power\": 9420156337, \ - \"voting_public_key\": \"0x3f656a1ba4ea8b33c81961fee6f15f09600f024435b1a7ada1e5b77b03a41a6d\" \ - }, \ - { \ - \"reward_address\": \"0xe1fff825e1bf009d35d9160f6340250b581f5d37c17538e960c0410b20\", \ - \"stake_public_key\": \"0x66ae1553036548b99b93c783811bb281be5a196a12d950bda4ac9b83630afbd1\", \ - \"voting_power\": 82168168290, \ - \"voting_public_key\": \"0x125860fc4870bb480d1d2a97f101e1c5c845c0222400fdaba7bcca93e79bd66e\" \ - } \ - ]"; - write_snapshot(content.to_string(), &self.out_file); - } -} - -pub fn write_snapshot>(content: String, path: P) { - use std::io::Write; - let mut file = std::fs::File::create(&path).unwrap(); - file.write_all(content.as_bytes()).unwrap(); -} diff --git a/snapshot-trigger-service/src/client/mod.rs b/snapshot-trigger-service/src/client/mod.rs index 43c3deee..3b3e9f9a 100644 --- a/snapshot-trigger-service/src/client/mod.rs +++ b/snapshot-trigger-service/src/client/mod.rs @@ -21,7 +21,7 @@ pub fn do_snapshot, P: Into>( SnapshotRestClient::new_with_token(snapshot_token.into(), snapshot_address.into()); println!("Snapshot params: {:?}", job_params); - let snapshot_job_id = snapshot_client.job_new(job_params).unwrap(); + let snapshot_job_id = snapshot_client.job_new(job_params.clone()).unwrap(); let wait = WaitBuilder::new().tries(10).sleep_between_tries(10).build(); println!("waiting for snapshot job"); @@ -29,7 +29,10 @@ pub fn do_snapshot, P: Into>( snapshot_client.wait_for_job_finish(snapshot_job_id.clone(), wait)?; println!("Snapshot done: {:?}", snapshot_jobs_status); - let snapshot = snapshot_client.get_snapshot(snapshot_job_id)?; + let snapshot = snapshot_client.get_snapshot( + snapshot_job_id, + job_params.tag.unwrap_or_else(|| "".to_string()), + )?; Ok(SnapshotResult { status: snapshot_jobs_status, @@ -39,6 +42,7 @@ pub fn do_snapshot, P: Into>( pub fn get_snapshot_by_id, S: Into, P: Into>( job_id: Q, + tag: Q, snapshot_token: S, snapshot_address: P, ) -> Result { @@ -46,7 +50,7 @@ pub fn get_snapshot_by_id, S: Into, P: Into>( SnapshotRestClient::new_with_token(snapshot_token.into(), snapshot_address.into()); let job_id = job_id.into(); - let snapshot = snapshot_client.get_snapshot(job_id.clone())?; + let snapshot = snapshot_client.get_snapshot(job_id.clone(), tag.into())?; let status = snapshot_client.job_status(job_id)?; Ok(SnapshotResult { @@ -57,6 +61,7 @@ pub fn get_snapshot_by_id, S: Into, P: Into>( pub fn get_snapshot_from_history_by_id, S: Into, P: Into>( job_id: Q, + tag: Q, snapshot_token: S, snapshot_address: P, ) -> Result { @@ -64,7 +69,7 @@ pub fn get_snapshot_from_history_by_id, S: Into, P: Into SnapshotRestClient::new_with_token(snapshot_token.into(), snapshot_address.into()); let job_id = job_id.into(); - let snapshot = snapshot_client.get_snapshot(job_id.clone())?; + let snapshot = snapshot_client.get_snapshot(job_id.clone(), tag.into())?; let status = snapshot_client.get_status(job_id)?; Ok(SnapshotResult { @@ -137,4 +142,6 @@ pub enum Error { ChainError(#[from] chain_addr::Error), #[error("serialization error")] SerdeError(#[from] serde_json::Error), + #[error(transparent)] + Config(#[from] crate::config::Error), } diff --git a/snapshot-trigger-service/src/client/rest.rs b/snapshot-trigger-service/src/client/rest.rs index c1658fb7..6847cc08 100644 --- a/snapshot-trigger-service/src/client/rest.rs +++ b/snapshot-trigger-service/src/client/rest.rs @@ -35,7 +35,7 @@ impl SnapshotRestClient { } fn path>(&self, path: S) -> String { - format!("{}/{}", self.address, path.into()) + format!("{}/{}", self.address, path.into().replace('\"', "")) } fn get>(&self, local_path: S) -> Result { @@ -68,13 +68,21 @@ impl SnapshotRestClient { pub fn download_snapshot, P: AsRef>( &self, id: S, + tag: S, output: P, ) -> Result<(), Error> { - self.download(format!("{}/snapshot.json", id.into()), output) + self.download( + format!("{}/{}_snapshot.json", id.into(), tag.into()), + output, + ) } - pub fn get_snapshot>(&self, id: S) -> Result { - self.get(format!("api/job/files/get/{}/snapshot.json", id.into())) + pub fn get_snapshot>(&self, id: S, tag: S) -> Result { + self.get(format!( + "api/job/files/get/{}/{}_snapshot.json", + id.into().replace("'\"'", ""), + tag.into() + )) } pub fn download_job_status, P: AsRef>( @@ -95,7 +103,10 @@ impl SnapshotRestClient { sub_location: S, output: P, ) -> Result<(), Error> { - let content = self.get(format!("api/job/files/get/{}", sub_location.into()))?; + let content = self.get(format!( + "api/job/files/get/{}", + sub_location.into().replace("'\"'", "") + ))?; let mut file = std::fs::File::create(&output)?; file.write_all(content.as_bytes())?; Ok(()) diff --git a/snapshot-trigger-service/src/config/job.rs b/snapshot-trigger-service/src/config/job.rs index 7d12d065..0db0b61c 100644 --- a/snapshot-trigger-service/src/config/job.rs +++ b/snapshot-trigger-service/src/config/job.rs @@ -6,3 +6,19 @@ pub struct JobParameters { pub slot_no: Option, pub tag: Option, } + +impl JobParameters { + pub fn daily() -> Self { + Self { + slot_no: None, + tag: Some("daily".to_string()), + } + } + + pub fn fund>(fund: S) -> Self { + Self { + slot_no: None, + tag: Some(fund.into()), + } + } +} diff --git a/snapshot-trigger-service/src/config/mod.rs b/snapshot-trigger-service/src/config/mod.rs index 963cc0fb..9f462ec1 100644 --- a/snapshot-trigger-service/src/config/mod.rs +++ b/snapshot-trigger-service/src/config/mod.rs @@ -1,6 +1,9 @@ mod job; +use assert_fs::fixture::PathChild; +use assert_fs::TempDir; pub use job::JobParameters; +use mainnet_tools::voting_tools::VotingToolsMock; use serde::{Deserialize, Serialize}; use std::path::Path; use std::path::PathBuf; @@ -64,6 +67,55 @@ impl Configuration { } } +#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +pub struct ConfigurationBuilder { + configuration: Configuration, +} + +impl ConfigurationBuilder { + pub fn with_port(mut self, port: u16) -> Self { + self.configuration.port = port; + self + } + + pub fn with_result_dir>(mut self, path: P) -> Self { + self.configuration.result_dir = path.as_ref().to_path_buf(); + self + } + + pub fn with_tmp_result_dir(self, tmp: &TempDir) -> Self { + self.with_result_dir(tmp.child("snapshot_result").path()) + } + + pub fn with_voting_tools_params(mut self, voting_tools: VotingToolsParams) -> Self { + self.configuration.voting_tools = voting_tools; + self + } + + pub fn build(self) -> Configuration { + self.configuration + } +} + +impl Default for Configuration { + fn default() -> Self { + Self { + port: 3030, + voting_tools: VotingToolsParams { + bin: None, + nix_branch: None, + network: NetworkType::Mainnet, + db: "".to_string(), + db_user: "".to_string(), + db_host: "".to_string(), + scale: 1_000_000, + }, + result_dir: Path::new(".").to_path_buf(), + token: None, + } + } +} + #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] pub struct VotingToolsParams { /// binary name @@ -85,6 +137,22 @@ pub struct VotingToolsParams { pub scale: u32, } +impl From for VotingToolsParams { + fn from(voting_tools_mock: VotingToolsMock) -> Self { + let config = mainnet_tools::db_sync::Settings::default(); + + Self { + bin: Some(voting_tools_mock.path().to_str().unwrap().to_string()), + nix_branch: None, + network: NetworkType::Mainnet, + db: config.db_name, + db_user: config.db_user, + db_host: config.db_host, + scale: 1_000_000, + } + } +} + impl VotingToolsParams { pub fn command(&self) -> Result { if let Some(bin) = &self.bin { @@ -113,6 +181,13 @@ pub fn read_config>(config: P) -> Result { serde_json::from_str(&contents).map_err(Into::into) } +pub fn write_config>(config: Configuration, path: P) -> Result<(), Error> { + use std::io::Write; + let mut file = std::fs::File::create(&path)?; + file.write_all(serde_json::to_string(&config)?.as_bytes()) + .map_err(Into::into) +} + #[derive(Debug, Error)] pub enum Error { #[error("cannot parse configuration")]