Files
nym/nym-api/src/node_describe_cache/provider.rs
T
Jędrzej Stuczyński d8c84cc4d6 feat: key rotation (#5777)
* wip

* wip: wrap node's sphinx key with a manager

* wip: choosing correct key for packet processing

* further propagation of key rotation information

* attaching key rotation information to reply surbs

* added basic key rotation information to mixnet contract

* wip: introducing cached queries for key rotation info from nym api

* unified nym-api contract cache refreshing

* finish packet decoding

* multi api client + retrieving rotation id

* rotating sphinx key files

* logic for migrating config file

* wip: putting new sphinx keys to self described endpoints

* processing loop of KeyRotationController

* fixed sphinx key loading

* rotating bloomfilters

* wired up KeyRotationController

* flushing bloomfilters to disk and loading

* most of nym-node changes

* post rebase fixes

* fixes due to backwards compatible hostkeys

* split http state.rs file

* dont use deprecated fields

* fixed backwards compatible deserialisation of host information

* split up node describe cache

* added a dedicated CacheRefresher listener to perform full refresh outside the set interval

* controlling announced sphinx keys within nym-api

* retrieving rotation id when pulling topology

* split nym-nodes http handlers

* v2 nym-api endpoints to retrieve nodes with additional metadata information

* bug fixes...

* additional bugfixes and guards against stuck epoch

* testnet manager: set first nym-api as the rewarder

* fixed host information deserialisation

* fixed panic during first key rotation

* post rebase fixes

* clippy

* more guards against stuck epochs

* added helper method to reset node's sphinx key

* instantiate mixnet contract with custom key rotation validity

* additional bugfixes and debugging nym-api deadlock

* passing shutdown to nym apis client

* remove dead test

* post rebasing fixes

* missing MixnetQueryClient variants

* remove usage of deprecated methods in sdk example

* fix: incorrect method signature

* post rebasing fixes

* attempt to retrieve key rotation id before doing any config migration work

* ignore tests relying on networking behaviour

* allow networking failures in certain tests
2025-06-03 11:22:51 +01:00

155 lines
5.0 KiB
Rust

// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node_describe_cache::cache::DescribedNodes;
use crate::node_describe_cache::refresh::RefreshData;
use crate::node_describe_cache::NodeDescribeCacheError;
use crate::nym_contract_cache::cache::NymContractCache;
use crate::support::caching::cache::SharedCache;
use crate::support::caching::refresher::{CacheItemProvider, CacheRefresher};
use crate::support::config;
use crate::support::config::DEFAULT_NODE_DESCRIBE_BATCH_SIZE;
use async_trait::async_trait;
use futures::{stream, StreamExt};
use std::collections::HashMap;
use tracing::{error, info};
pub struct NodeDescriptionProvider {
contract_cache: NymContractCache,
allow_all_ips: bool,
batch_size: usize,
}
impl NodeDescriptionProvider {
pub(crate) fn new(
contract_cache: NymContractCache,
allow_all_ips: bool,
) -> NodeDescriptionProvider {
NodeDescriptionProvider {
contract_cache,
allow_all_ips,
batch_size: DEFAULT_NODE_DESCRIBE_BATCH_SIZE,
}
}
#[must_use]
pub(crate) fn with_batch_size(mut self, batch_size: usize) -> Self {
self.batch_size = batch_size;
self
}
}
#[async_trait]
impl CacheItemProvider for NodeDescriptionProvider {
type Item = DescribedNodes;
type Error = NodeDescribeCacheError;
async fn wait_until_ready(&self) {
self.contract_cache.naive_wait_for_initial_values().await
}
async fn try_refresh(&self) -> Result<Self::Item, Self::Error> {
// we need to query:
// - legacy mixnodes (because they might already be running nym-nodes, but haven't updated contract info)
// - legacy gateways (because they might already be running nym-nodes, but haven't updated contract info)
// - nym-nodes
let mut nodes_to_query: Vec<RefreshData> = Vec::new();
match self.contract_cache.all_cached_legacy_mixnodes().await {
None => error!("failed to obtain mixnodes information from the cache"),
Some(legacy_mixnodes) => {
for node in &**legacy_mixnodes {
if let Ok(data) = node.try_into() {
nodes_to_query.push(data);
}
}
}
}
match self.contract_cache.all_cached_legacy_gateways().await {
None => error!("failed to obtain gateways information from the cache"),
Some(legacy_gateways) => {
for node in &**legacy_gateways {
if let Ok(data) = node.try_into() {
nodes_to_query.push(data);
}
}
}
}
match self.contract_cache.all_cached_nym_nodes().await {
None => error!("failed to obtain nym-nodes information from the cache"),
Some(nym_nodes) => {
for node in &**nym_nodes {
if let Ok(data) = node.try_into() {
nodes_to_query.push(data);
}
}
}
}
let nodes = stream::iter(
nodes_to_query
.into_iter()
.map(|n| n.try_refresh(self.allow_all_ips)),
)
.buffer_unordered(self.batch_size)
.filter_map(|x| async move { x.map(|d| (d.node_id, d)) })
.collect::<HashMap<_, _>>()
.await;
let mut addresses_cache = HashMap::new();
for node in nodes.values() {
for ip in &node.description.host_information.ip_address {
addresses_cache.insert(*ip, node.node_id);
}
}
info!("refreshed self described data for {} nodes", nodes.len());
info!("with {} unique ip addresses", addresses_cache.len());
Ok(DescribedNodes {
nodes,
addresses_cache,
})
}
}
// currently dead code : (
#[allow(dead_code)]
pub(crate) fn new_refresher(
config: &config::TopologyCacher,
contract_cache: NymContractCache,
) -> CacheRefresher<DescribedNodes, NodeDescribeCacheError> {
CacheRefresher::new(
Box::new(
NodeDescriptionProvider::new(
contract_cache,
config.debug.node_describe_allow_illegal_ips,
)
.with_batch_size(config.debug.node_describe_batch_size),
),
config.debug.node_describe_caching_interval,
)
}
pub(crate) fn new_provider_with_initial_value(
config: &config::TopologyCacher,
contract_cache: NymContractCache,
initial: SharedCache<DescribedNodes>,
) -> CacheRefresher<DescribedNodes, NodeDescribeCacheError> {
CacheRefresher::new_with_initial_value(
Box::new(
NodeDescriptionProvider::new(
contract_cache,
config.debug.node_describe_allow_illegal_ips,
)
.with_batch_size(config.debug.node_describe_batch_size),
),
config.debug.node_describe_caching_interval,
initial,
)
}