From 4f6d91037ef6781801349238e30e8382e254a83a Mon Sep 17 00:00:00 2001 From: Luke Parker <lukeparker5132@gmail.com> Date: Thu, 29 Aug 2024 16:27:00 -0400 Subject: [PATCH] Call flush_key --- processor/scanner/src/db.rs | 36 +++++++++++++++++--- processor/scanner/src/eventuality/mod.rs | 9 ++++- processor/scanner/src/lifetime.rs | 43 +++++++++++++++--------- spec/processor/Multisig Rotation.md | 3 +- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/processor/scanner/src/db.rs b/processor/scanner/src/db.rs index a6272eeb..20aa2999 100644 --- a/processor/scanner/src/db.rs +++ b/processor/scanner/src/db.rs @@ -11,7 +11,8 @@ use serai_coins_primitives::OutInstructionWithBalance; use primitives::{EncodableG, Address, ReceivedOutput}; use crate::{ - lifetime::LifetimeStage, ScannerFeed, KeyFor, AddressFor, OutputFor, Return, + lifetime::{LifetimeStage, Lifetime}, + ScannerFeed, KeyFor, AddressFor, OutputFor, Return, scan::next_to_scan_for_outputs_block, }; @@ -30,6 +31,7 @@ pub(crate) struct SeraiKey<K> { pub(crate) stage: LifetimeStage, pub(crate) activation_block_number: u64, pub(crate) block_at_which_reporting_starts: u64, + pub(crate) block_at_which_forwarding_starts: Option<u64>, } pub(crate) struct OutputWithInInstruction<S: ScannerFeed> { @@ -82,7 +84,7 @@ create_db!( /* A block is notable if one of three conditions are met: - 1) We activated a key within this block. + 1) We activated a key within this block (or explicitly forward to an activated key). 2) We retired a key within this block. 3) We received outputs within this block. @@ -120,9 +122,32 @@ impl<S: ScannerFeed> ScannerGlobalDb<S> { // TODO: Panic if we've ever seen this key before - // Push the key + // Fetch the existing keys let mut keys: Vec<SeraiKeyDbEntry<EncodableG<KeyFor<S>>>> = ActiveKeys::get(txn).unwrap_or(vec![]); + + // If this new key retires a key, mark the block at which forwarding explicitly occurs notable + // This lets us obtain synchrony over the transactions we'll make to accomplish this + if let Some(key_retired_by_this) = keys.last() { + NotableBlock::set( + txn, + Lifetime::calculate::<S>( + // The 'current block number' used for this calculation + activation_block_number, + // The activation block of the key we're getting the lifetime of + key_retired_by_this.activation_block_number, + // The activation block of the key which will retire this key + Some(activation_block_number), + ) + .block_at_which_forwarding_starts + .expect( + "didn't calculate the block forwarding starts at despite passing the next key's info", + ), + &(), + ); + } + + // Push and save the next key keys.push(SeraiKeyDbEntry { activation_block_number, key: EncodableG(key) }); ActiveKeys::set(txn, &keys); } @@ -185,8 +210,8 @@ impl<S: ScannerFeed> ScannerGlobalDb<S> { if block_number < raw_keys[i].activation_block_number { continue; } - let (stage, block_at_which_reporting_starts) = - LifetimeStage::calculate_stage_and_reporting_start_block::<S>( + let Lifetime { stage, block_at_which_reporting_starts, block_at_which_forwarding_starts } = + Lifetime::calculate::<S>( block_number, raw_keys[i].activation_block_number, raw_keys.get(i + 1).map(|key| key.activation_block_number), @@ -196,6 +221,7 @@ impl<S: ScannerFeed> ScannerGlobalDb<S> { stage, activation_block_number: raw_keys[i].activation_block_number, block_at_which_reporting_starts, + block_at_which_forwarding_starts, }); } assert!(keys.len() <= 2, "more than two keys active"); diff --git a/processor/scanner/src/eventuality/mod.rs b/processor/scanner/src/eventuality/mod.rs index c5f93789..002131cc 100644 --- a/processor/scanner/src/eventuality/mod.rs +++ b/processor/scanner/src/eventuality/mod.rs @@ -341,8 +341,15 @@ impl<D: Db, S: ScannerFeed, Sch: Scheduler<S>> ContinuallyRan for EventualityTas intake_eventualities::<S>(&mut txn, new_eventualities); } - // Now that we've intaked any Eventualities caused, check if we're retiring any keys for key in &keys { + // If this is the block at which forwarding starts for this key, flush it + // We do this after we issue the above update for any efficiencies gained by doing so + if key.block_at_which_forwarding_starts == Some(b) { + assert!(key.key != keys.last().unwrap().key); + self.scheduler.flush_key(&mut txn, key.key, keys.last().unwrap().key); + } + + // Now that we've intaked any Eventualities caused, check if we're retiring any keys if key.stage == LifetimeStage::Finishing { let eventualities = EventualityDb::<S>::eventualities(&txn, key.key); // TODO: This assumes the Scheduler is empty diff --git a/processor/scanner/src/lifetime.rs b/processor/scanner/src/lifetime.rs index 09df7a37..e15c0f55 100644 --- a/processor/scanner/src/lifetime.rs +++ b/processor/scanner/src/lifetime.rs @@ -35,17 +35,25 @@ pub(crate) enum LifetimeStage { Finishing, } -impl LifetimeStage { - /// Get the stage of its lifetime this multisig is in, and the block at which we start reporting - /// outputs to it. +/// 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<u64>, +} + +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_stage_and_reporting_start_block<S: ScannerFeed>( + pub(crate) fn calculate<S: ScannerFeed>( block_number: u64, activation_block_number: u64, next_keys_activation_block_number: Option<u64>, - ) -> (Self, u64) { + ) -> Self { assert!( activation_block_number >= block_number, "calculating lifetime stage for an inactive multisig" @@ -55,14 +63,14 @@ impl LifetimeStage { 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 reporting_start_block = active_yet_not_reporting_end_block; + let block_at_which_reporting_starts = active_yet_not_reporting_end_block; if block_number < active_yet_not_reporting_end_block { - return (LifetimeStage::ActiveYetNotReporting, reporting_start_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 (LifetimeStage::Active, reporting_start_block); + return Lifetime { stage: LifetimeStage::Active, block_at_which_reporting_starts, block_at_which_forwarding_starts: None }; }; assert!( @@ -70,19 +78,22 @@ impl LifetimeStage { "next set of keys activated before this multisig activated" ); - // If the new multisig is still having its activation block finalized on-chain, this multisig - // is still active (step 3) 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 (LifetimeStage::Active, reporting_start_block); + return Lifetime { stage: LifetimeStage::Active, block_at_which_reporting_starts, block_at_which_forwarding_starts }; } // Step 4 details a further CONFIRMATIONS - let new_active_and_used_for_change_end_block = - new_active_yet_not_reporting_end_block + S::CONFIRMATIONS; if block_number < new_active_and_used_for_change_end_block { - return (LifetimeStage::UsingNewForChange, reporting_start_block); + return Lifetime { stage: LifetimeStage::UsingNewForChange, block_at_which_reporting_starts, block_at_which_forwarding_starts }; } // Step 5 details a further 6 hours @@ -90,10 +101,10 @@ impl LifetimeStage { 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 (LifetimeStage::Forwarding, reporting_start_block); + return Lifetime { stage: LifetimeStage::Forwarding, block_at_which_reporting_starts, block_at_which_forwarding_starts }; } // Step 6 - (LifetimeStage::Finishing, reporting_start_block) + Lifetime { stage: LifetimeStage::Finishing, block_at_which_reporting_starts, block_at_which_forwarding_starts } } } diff --git a/spec/processor/Multisig Rotation.md b/spec/processor/Multisig Rotation.md index ff5c3d28..916ce56b 100644 --- a/spec/processor/Multisig Rotation.md +++ b/spec/processor/Multisig Rotation.md @@ -102,7 +102,8 @@ The following timeline is established: 5) For the next 6 hours, all non-`Branch` outputs received are immediately forwarded to the new multisig. Only external transactions to the new multisig - are included in `Batch`s. + are included in `Batch`s. Any outputs not yet transferred as change are + explicitly transferred. The new multisig infers the `InInstruction`, and refund address, for forwarded `External` outputs via reading what they were for the original