From 41ac8667292cd0a199843ceeb0b5311726c26cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogdan-=C8=98tefan=20Neac=C5=9Fu?= Date: Thu, 11 Aug 2022 11:58:17 +0300 Subject: [PATCH] Feature/validator api panics (#1520) * Coconut unwraps * Fail softly on epoch queries * Update changelog * Retry a few times before failing as before --- CHANGELOG.md | 2 + validator-api/Cargo.toml | 4 +- validator-api/src/coconut/error.rs | 3 + validator-api/src/coconut/mod.rs | 11 ++-- validator-api/src/rewarded_set_updater/mod.rs | 65 +++++++++++++++---- 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee72b3c54a..8af0040206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// - All binaries and cosmwasm blobs are configured at runtime now; binaries are configured using environment variables or .env files and contracts keep the configuration parameters in storage ([#1463]) - gateway, network-statistics: include gateway id in the sent statistical data ([#1478]) - network explorer: tweak how active set probability is shown ([#1503]) +- validator-api: rewarder set update fails without panicking on possible nymd queries ([#1520]) [#1249]: https://github.com/nymtech/nym/pull/1249 @@ -81,6 +82,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// [#1487]: https://github.com/nymtech/nym/pull/1487 [#1496]: https://github.com/nymtech/nym/pull/1496 [#1503]: https://github.com/nymtech/nym/pull/1503 +[#1520]: https://github.com/nymtech/nym/pull/1520 ## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22) diff --git a/validator-api/Cargo.toml b/validator-api/Cargo.toml index 8ec660cdb7..c741957303 100644 --- a/validator-api/Cargo.toml +++ b/validator-api/Cargo.toml @@ -58,6 +58,7 @@ gateway-client = { path="../common/client-libs/gateway-client" } mixnet-contract-common = { path= "../common/cosmwasm-smart-contracts/mixnet-contract" } multisig-contract-common = { path = "../common/cosmwasm-smart-contracts/multisig-contract" } nymsphinx = { path="../common/nymsphinx" } +nymcoconut = { path = "../common/nymcoconut", optional = true } task = { path = "../common/task" } topology = { path="../common/topology" } validator-api-requests = { path = "validator-api-requests" } @@ -71,7 +72,7 @@ console-subscriber = { version = "0.1.1", optional = true} cfg-if = "1.0" [features] -coconut = ["coconut-interface", "credentials", "gateway-client/coconut", "credentials/coconut", "validator-api-requests/coconut"] +coconut = ["coconut-interface", "credentials", "gateway-client/coconut", "credentials/coconut", "validator-api-requests/coconut", "nymcoconut"] no-reward = [] generate-ts = [] @@ -82,6 +83,5 @@ vergen = { version = "5", default-features = false, features = ["build", "git", [dev-dependencies] attohttpc = {version = "0.18.0", features = ["json"]} -nymcoconut = { path = "../common/nymcoconut" } cw3 = "0.13.2" cw-utils = "0.13.2" diff --git a/validator-api/src/coconut/error.rs b/validator-api/src/coconut/error.rs index a1915dac17..7dfdb843c2 100644 --- a/validator-api/src/coconut/error.rs +++ b/validator-api/src/coconut/error.rs @@ -31,6 +31,9 @@ pub enum CoconutError { #[error("Nymd error - {0}")] NymdError(#[from] NymdError), + #[error("Coconut internal error - {0}")] + CoconutInternalError(#[from] nymcoconut::CoconutError), + #[error("Could not find a deposit event in the transaction provided")] DepositEventNotFound, diff --git a/validator-api/src/coconut/mod.rs b/validator-api/src/coconut/mod.rs index f14ae1c501..efd1728a7b 100644 --- a/validator-api/src/coconut/mod.rs +++ b/validator-api/src/coconut/mod.rs @@ -192,15 +192,14 @@ impl InternalSignRequest { } } -fn blind_sign(request: InternalSignRequest, key_pair: &KeyPair) -> BlindedSignature { - let params = Parameters::new(request.total_params()).unwrap(); - coconut_interface::blind_sign( +fn blind_sign(request: InternalSignRequest, key_pair: &KeyPair) -> Result { + let params = Parameters::new(request.total_params())?; + Ok(coconut_interface::blind_sign( ¶ms, &key_pair.secret_key(), request.blind_sign_request(), request.public_attributes(), - ) - .unwrap() + )?) } #[post("/blind-sign", data = "")] @@ -226,7 +225,7 @@ pub async fn post_blind_sign( blind_sign_request_body.public_attributes(), blind_sign_request_body.blind_sign_request().clone(), ); - let blinded_signature = blind_sign(internal_request, &state.key_pair); + let blinded_signature = blind_sign(internal_request, &state.key_pair)?; let response = state .encrypt_and_store( diff --git a/validator-api/src/rewarded_set_updater/mod.rs b/validator-api/src/rewarded_set_updater/mod.rs index d6006d2617..627452d673 100644 --- a/validator-api/src/rewarded_set_updater/mod.rs +++ b/validator-api/src/rewarded_set_updater/mod.rs @@ -22,6 +22,7 @@ use mixnet_contract_common::{IdentityKey, Interval, MixNodeBond}; use rand::prelude::SliceRandom; use rand::rngs::OsRng; use std::collections::HashSet; +use std::ops::Deref; use std::time::Duration; use time::OffsetDateTime; use tokio::time::sleep; @@ -58,7 +59,44 @@ impl MixnodeToReward { } // Epoch has all the same semantics as interval, but has a lower set duration -type Epoch = Interval; +pub struct Epoch(Interval); + +impl Epoch { + pub(crate) async fn update_to_latest( + &mut self, + rewarded_set_updater: &RewardedSetUpdater, + ) -> Result<(), RewardingError> { + let mut wait_time = 2; + let mut ret = Ok(()); + for _ in 0..3 { + match rewarded_set_updater.nymd_client.get_current_epoch().await { + Ok(epoch) => { + *self = Epoch(epoch); + return Ok(()); + } + Err(e) => { + warn!( + "Could not update epoch: {:?}. Retrying again in {} seconds", + e, wait_time + ); + ret = Err(e); + } + }; + sleep(Duration::from_secs(wait_time)).await; + wait_time *= wait_time; + } + error!("Failed to update epoch. Exiting now"); + Ok(ret?) + } +} + +impl Deref for Epoch { + type Target = Interval; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} pub struct RewardedSetUpdater { nymd_client: Client, @@ -68,7 +106,7 @@ pub struct RewardedSetUpdater { impl RewardedSetUpdater { pub(crate) async fn epoch(&self) -> Result { - Ok(self.nymd_client.get_current_epoch().await?) + Ok(Epoch(self.nymd_client.get_current_epoch().await?)) } pub(crate) async fn new( @@ -120,9 +158,10 @@ impl RewardedSetUpdater { async fn reward_current_rewarded_set( &self, + epoch: &mut Epoch, ) -> Result)>, RewardingError> { - let to_reward = self.nodes_to_reward().await?; - let epoch = self.epoch().await?; + let to_reward = self.nodes_to_reward(epoch).await?; + epoch.update_to_latest(self).await?; // self.storage.insert_started_epoch_rewarding(epoch).await?; @@ -157,8 +196,11 @@ impl RewardedSetUpdater { } } - async fn nodes_to_reward(&self) -> Result, RewardingError> { - let epoch = self.epoch().await?; + async fn nodes_to_reward( + &self, + epoch: &mut Epoch, + ) -> Result, RewardingError> { + epoch.update_to_latest(self).await?; let active_set = self .validator_cache .active_set_detailed() @@ -200,8 +242,8 @@ impl RewardedSetUpdater { } // This is where the epoch gets advanced, and all epoch related transactions originate - async fn update(&self) -> Result<(), RewardingError> { - let epoch = self.epoch().await?; + async fn update(&self, epoch: &mut Epoch) -> Result<(), RewardingError> { + epoch.update_to_latest(self).await?; log::info!("Starting rewarded set update"); // we know the entries are not stale, as a matter of fact they were JUST updated, since we got notified let all_nodes = self.validator_cache.mixnodes().await; @@ -218,7 +260,7 @@ impl RewardedSetUpdater { // log::info!("Rewarded current rewarded set... SUCCESS"); // } - let reward_msgs = self.reward_current_rewarded_set().await?; + let reward_msgs = self.reward_current_rewarded_set(epoch).await?; let rewarded_set_size = epoch_reward_params.rewarded_set_size() as u32; let active_set_size = epoch_reward_params.active_set_size() as u32; @@ -288,11 +330,12 @@ impl RewardedSetUpdater { pub(crate) async fn run(&mut self) -> Result<(), RewardingError> { self.validator_cache.wait_for_initial_values().await; + let mut epoch = self.epoch().await?; loop { // wait until the cache refresher determined its time to update the rewarded/active sets let time = OffsetDateTime::now_utc().unix_timestamp(); - let epoch = self.epoch().await?; + epoch.update_to_latest(self).await?; let time_to_epoch_change = epoch.end_unix_timestamp() - time; if time_to_epoch_change <= 0 { self.update_blacklist(&epoch).await?; @@ -300,7 +343,7 @@ impl RewardedSetUpdater { "Time to epoch change is {}, updating rewarded set", time_to_epoch_change ); - self.update().await?; + self.update(&mut epoch).await?; } else { log::info!( "Waiting for epoch change, time to epoch change is {}",