ic-query 0.55.3

Internet Computer query library for CloudEngine, NNS, SNS, ICRC, system canisters, and public network metadata
Documentation
use super::{FixtureSnsDiscoverySource, GOVERNANCE_A, ROOT_A, fixture_sns_governance_parameters};
use crate::sns::report::SNS_REWARD_CHECKPOINT_PAGE_SIZE;
use crate::sns::report::tests::*;
use std::{collections::VecDeque, sync::Mutex};

///
/// FixtureSnsRewardSource
///
/// Stable bracketed reward source with configurable strict neuron pages.
///

pub(in crate::sns::report::tests) struct FixtureSnsRewardSource {
    calls: Mutex<Vec<&'static str>>,
    pages: Mutex<VecDeque<MainnetSnsRewardNeuronPage>>,
    unstable_component: Option<&'static str>,
    version_calls: Mutex<u32>,
    parameter_calls: Mutex<u32>,
    event_calls: Mutex<u32>,
    max_number_of_neurons: Option<u64>,
}

impl FixtureSnsRewardSource {
    pub(in crate::sns::report::tests) fn new(pages: Vec<MainnetSnsRewardNeuronPage>) -> Self {
        Self {
            calls: Mutex::new(Vec::new()),
            pages: Mutex::new(pages.into()),
            unstable_component: None,
            version_calls: Mutex::new(0),
            parameter_calls: Mutex::new(0),
            event_calls: Mutex::new(0),
            max_number_of_neurons: Some(200_000),
        }
    }

    pub(in crate::sns::report::tests) fn unstable(
        pages: Vec<MainnetSnsRewardNeuronPage>,
        component: &'static str,
    ) -> Self {
        Self {
            unstable_component: Some(component),
            ..Self::new(pages)
        }
    }

    pub(in crate::sns::report::tests) fn calls(&self) -> Vec<&'static str> {
        self.calls.lock().unwrap().clone()
    }

    pub(in crate::sns::report::tests) const fn with_max_number_of_neurons(
        mut self,
        max_number_of_neurons: Option<u64>,
    ) -> Self {
        self.max_number_of_neurons = max_number_of_neurons;
        self
    }
}

delegate_sns_discovery!(FixtureSnsRewardSource);

impl SnsRewardSource for FixtureSnsRewardSource {
    fn fetch_sns_reward_running_version<'a>(
        &'a self,
        _request: &'a SnsSourceRequest,
        sns: &'a MainnetSns,
    ) -> crate::report::SourceFuture<'a, Result<SnsRunningVersionResponse, SnsHostError>> {
        Box::pin(async move {
            assert_eq!(sns.governance_canister_id, GOVERNANCE_A);
            self.calls.lock().unwrap().push("version");
            let call = *self.version_calls.lock().unwrap();
            (*self.version_calls.lock().unwrap() = call + 1);
            let mut response = fixture_reward_running_version();
            if self.unstable_component == Some("version") && call > 0 {
                response.pending_version = None;
            }
            Ok(response)
        })
    }

    fn fetch_sns_reward_parameters<'a>(
        &'a self,
        _request: &'a SnsSourceRequest,
        _sns: &'a MainnetSns,
    ) -> crate::report::SourceFuture<'a, Result<SnsGovernanceParameters, SnsHostError>> {
        Box::pin(async move {
            self.calls.lock().unwrap().push("parameters");
            let call = *self.parameter_calls.lock().unwrap();
            (*self.parameter_calls.lock().unwrap() = call + 1);
            let mut parameters = fixture_sns_governance_parameters();
            parameters.max_number_of_neurons = self.max_number_of_neurons;
            if self.unstable_component == Some("parameters") && call > 0 {
                parameters.transaction_fee_e8s = Some(20_000);
            }
            Ok(parameters)
        })
    }

    fn fetch_sns_reward_event<'a>(
        &'a self,
        _request: &'a SnsSourceRequest,
        _sns: &'a MainnetSns,
    ) -> crate::report::SourceFuture<'a, Result<SnsRewardEvent, SnsHostError>> {
        Box::pin(async move {
            self.calls.lock().unwrap().push("event");
            let call = *self.event_calls.lock().unwrap();
            (*self.event_calls.lock().unwrap() = call + 1);
            let mut event = fixture_reward_event();
            if self.unstable_component == Some("event") && call > 0 {
                event.distributed_e8s_equivalent += 1;
            }
            Ok(event)
        })
    }

    fn fetch_sns_reward_neuron_page<'a>(
        &'a self,
        _request: &'a SnsSourceRequest,
        _sns: &'a MainnetSns,
        limit: u32,
        _start_page_at: Option<&'a SnsNeuronId>,
    ) -> crate::report::SourceFuture<'a, Result<MainnetSnsRewardNeuronPage, SnsHostError>> {
        Box::pin(async move {
            assert_eq!(limit, SNS_REWARD_CHECKPOINT_PAGE_SIZE);
            self.calls.lock().unwrap().push("page");
            self.pages
                .lock()
                .unwrap()
                .pop_front()
                .ok_or_else(|| SnsHostError::InvalidSourceData {
                    capability: "fixture SNS reward checkpoint",
                    reason: "unexpected page request".to_string(),
                })
        })
    }
}

pub(in crate::sns::report::tests) const fn fixture_reward_page(
    rows: Vec<SnsRewardCheckpointRow>,
    next_cursor: Option<SnsNeuronId>,
) -> MainnetSnsRewardNeuronPage {
    MainnetSnsRewardNeuronPage {
        neurons: rows,
        next_cursor,
    }
}

pub(in crate::sns::report::tests) fn fixture_reward_row(seed: u8) -> SnsRewardCheckpointRow {
    let mut row = SnsRewardCheckpointRow {
        neuron_id: format!("{seed:02x}").repeat(32),
        created_timestamp_seconds: 1_700_000_000 + u64::from(seed),
        maturity_e8s_equivalent: u64::from(seed) * 100,
        staked_maturity_e8s_equivalent: Some(u64::from(seed) * 10),
        combined_maturity_e8s_equivalent: u64::from(seed) * 110,
        auto_stake_maturity: Some(seed.is_multiple_of(2)),
        permissions: vec![SnsNeuronPermissionRow {
            principal: Some(ROOT_A.to_string()),
            permission_types: vec![
                SnsNeuronPermissionValue::from_code(2),
                SnsNeuronPermissionValue::from_code(4),
            ],
        }],
        disburse_maturity_in_progress: Vec::new(),
        maturity_mint_conversion_observed_disabled: SnsPolicyObservationStatus::ObservedSatisfied,
        manual_maturity_staking_observed_disabled: SnsPolicyObservationStatus::ObservedSatisfied,
    };
    let (mint, staking) = row.derived_policy_observations();
    row.maturity_mint_conversion_observed_disabled = mint;
    row.manual_maturity_staking_observed_disabled = staking;
    row
}

fn fixture_reward_event() -> SnsRewardEvent {
    SnsRewardEvent {
        rounds_since_last_distribution: Some(1),
        actual_timestamp_seconds: 1_780_531_100,
        end_timestamp_seconds: Some(1_780_531_200),
        total_available_e8s_equivalent: Some(1_000),
        distributed_e8s_equivalent: 500,
        round: 42,
        settled_proposals: vec![SnsRewardProposalId { id: 7 }],
    }
}

fn fixture_reward_running_version() -> SnsRunningVersionResponse {
    SnsRunningVersionResponse {
        deployed_version: Some(fixture_reward_version(1)),
        pending_version: Some(SnsPendingUpgrade {
            mark_failed_at_seconds: 1_780_617_600,
            checking_upgrade_lock: 9,
            proposal_id: 42,
            target_version: Some(fixture_reward_version(11)),
        }),
    }
}

fn fixture_reward_version(seed: u8) -> SnsVersion {
    SnsVersion {
        archive_wasm_hash_hex: format!("{seed:02x}").repeat(32),
        root_wasm_hash_hex: format!("{:02x}", seed + 1).repeat(32),
        swap_wasm_hash_hex: format!("{:02x}", seed + 2).repeat(32),
        ledger_wasm_hash_hex: format!("{:02x}", seed + 3).repeat(32),
        governance_wasm_hash_hex: format!("{:02x}", seed + 4).repeat(32),
        index_wasm_hash_hex: format!("{:02x}", seed + 5).repeat(32),
    }
}