Call flush_key

This commit is contained in:
Luke Parker 2024-08-29 16:27:00 -04:00
parent 8db76ed67c
commit 4f6d91037e
4 changed files with 68 additions and 23 deletions

View file

@ -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");

View file

@ -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

View file

@ -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 }
}
}

View file

@ -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