use crate::ScannerFeed; /// An enum representing the stage of a multisig within its lifetime. /// /// This corresponds to `spec/processor/Multisig Rotation.md`, which details steps 1-8 of the /// rotation process. Steps 7-8 regard a multisig which isn't retiring yet retired, and /// accordingly, no longer exists, so they are not modelled here (as this only models active /// multisigs. Inactive multisigs aren't represented in the first place). #[derive(Clone, Copy, PartialEq, Debug)] pub enum LifetimeStage { /// A new multisig, once active, shouldn't actually start receiving coins until several blocks /// later. If any UI is premature in sending to this multisig, we delay to report the outputs to /// prevent some DoS concerns. /// /// This represents steps 1-3 for a new multisig. ActiveYetNotReporting, /// Active with all outputs being reported on-chain. /// /// This represents step 4 onwards for a new multisig. Active, /// Retiring with all outputs being reported on-chain. /// /// This represents step 4 for a retiring multisig. UsingNewForChange, /// Retiring with outputs being forwarded, reported on-chain once forwarded. /// /// This represents step 5 for a retiring multisig. Forwarding, /// Retiring with only existing obligations being handled. /// /// This represents step 6 for a retiring multisig. /// /// Steps 7 and 8 are represented by the retiring multisig no longer existing, and these states /// are only for multisigs which actively exist. Finishing, } /// The lifetime of the multisig, including various block numbers. pub(crate) struct Lifetime { pub(crate) stage: LifetimeStage, pub(crate) block_at_which_reporting_starts: u64, // This is only Some if the next key's activation block number is passed to calculate, and the // stage is at least `LifetimeStage::Active.` pub(crate) block_at_which_forwarding_starts: Option, } impl Lifetime { /// Get the lifetime of this multisig. /// /// Panics if the multisig being calculated for isn't actually active and a variety of other /// insane cases. pub(crate) fn calculate( block_number: u64, activation_block_number: u64, next_keys_activation_block_number: Option, ) -> Self { assert!( activation_block_number >= block_number, "calculating lifetime stage for an inactive multisig" ); // This is exclusive, not inclusive, since we want a CONFIRMATIONS + 10 minutes window and the // activation block itself is the first block within this window let active_yet_not_reporting_end_block = activation_block_number + S::CONFIRMATIONS + S::TEN_MINUTES; // The exclusive end block is the inclusive start block let block_at_which_reporting_starts = active_yet_not_reporting_end_block; if block_number < active_yet_not_reporting_end_block { return Lifetime { stage: LifetimeStage::ActiveYetNotReporting, block_at_which_reporting_starts, block_at_which_forwarding_starts: None, }; } let Some(next_keys_activation_block_number) = next_keys_activation_block_number else { // If there is no next multisig, this is the active multisig return Lifetime { stage: LifetimeStage::Active, block_at_which_reporting_starts, block_at_which_forwarding_starts: None, }; }; assert!( next_keys_activation_block_number > active_yet_not_reporting_end_block, "next set of keys activated before this multisig activated" ); let new_active_yet_not_reporting_end_block = next_keys_activation_block_number + S::CONFIRMATIONS + S::TEN_MINUTES; let new_active_and_used_for_change_end_block = new_active_yet_not_reporting_end_block + S::CONFIRMATIONS; // The exclusive end block is the inclusive start block let block_at_which_forwarding_starts = Some(new_active_and_used_for_change_end_block); // If the new multisig is still having its activation block finalized on-chain, this multisig // is still active (step 3) if block_number < new_active_yet_not_reporting_end_block { return Lifetime { stage: LifetimeStage::Active, block_at_which_reporting_starts, block_at_which_forwarding_starts, }; } // Step 4 details a further CONFIRMATIONS if block_number < new_active_and_used_for_change_end_block { return Lifetime { stage: LifetimeStage::UsingNewForChange, block_at_which_reporting_starts, block_at_which_forwarding_starts, }; } // Step 5 details a further 6 hours // 6 hours = 6 * 60 minutes = 6 * 6 * 10 minutes let new_active_and_forwarded_to_end_block = new_active_and_used_for_change_end_block + (6 * 6 * S::TEN_MINUTES); if block_number < new_active_and_forwarded_to_end_block { return Lifetime { stage: LifetimeStage::Forwarding, block_at_which_reporting_starts, block_at_which_forwarding_starts, }; } // Step 6 Lifetime { stage: LifetimeStage::Finishing, block_at_which_reporting_starts, block_at_which_forwarding_starts, } } }