mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-08-25 03:11:21 +00:00
refactor(lez/sequencer): config submission cooldown is expressed in bedrock slots instead of time
This commit is contained in:
@@ -146,13 +146,8 @@ const fn default_metrics_address() -> Option<SocketAddr> {
|
||||
Some(SequencerConfig::DEFAULT_METRICS_ADDRESS)
|
||||
}
|
||||
|
||||
/// Extra fee added to every funded Bedrock transaction.
|
||||
///
|
||||
/// Covers a possible rise in gas prices before it gets mined — a stuck
|
||||
/// transaction keeps retrying with the same fee, so if prices rise past this
|
||||
/// it never gets mined. 10k still covers a normal price jump on a block
|
||||
/// transaction; the sdk's default of 200 is too low for large transactions
|
||||
/// and can leave them stuck.
|
||||
/// Extra fee added to every funded Bedrock transaction, covering a gas price
|
||||
/// rise before it is mined.
|
||||
#[must_use]
|
||||
pub const fn default_priority_fee() -> u64 {
|
||||
10_000
|
||||
|
||||
@@ -87,6 +87,9 @@ const MAX_DISPATCHES_PER_BLOCK: usize = 16;
|
||||
// once that path exists, instead of a fixed genesis-only key.
|
||||
const GENESIS_STAKE_FUNDING_KEY: [u8; 32] = [9; 32];
|
||||
|
||||
/// A number of Bedrock slots, as opposed to a [`Slot`] position.
|
||||
type SlotCount = u64;
|
||||
|
||||
/// A founding sequencer's key, plus the ownership account attesting to its stake.
|
||||
type FoundingStake = (
|
||||
sequencer_stake_core::SequencerKey,
|
||||
@@ -132,23 +135,16 @@ pub struct SequencerCore<BP: BlockPublisherTrait = ZoneSdkPublisher> {
|
||||
/// store handle, so leaving them running would keep the `RocksDB` lock held
|
||||
/// and make the home directory unopenable by a restarting sequencer.
|
||||
watchers: TaskGroup,
|
||||
/// When the last committee-config submission went out, or `None` if none
|
||||
/// has. See [`Self::COMMITTEE_SUBMISSION_COOLDOWN`].
|
||||
last_committee_submission_at: Option<Instant>,
|
||||
/// Channel tip slot as of the last committee-config submission.
|
||||
last_committee_submission_slot: Option<Slot>,
|
||||
}
|
||||
|
||||
impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
||||
const CHANNEL_PROBE_RETRIES: usize = 29;
|
||||
const CHANNEL_PROBE_RETRY_DELAY: Duration = Duration::from_secs(2);
|
||||
/// Minimum wait between committee-config submission attempts.
|
||||
/// [`committee_discovery::committee_update`] re-detects the same mismatch
|
||||
/// on every block until Bedrock's live state catches up; without a
|
||||
/// cooldown that resubmits every block cycle, each attempt racing (and
|
||||
/// likely invalidating) the last one before it has any chance to land.
|
||||
/// Comfortably above the round-robin reclaim window
|
||||
/// (`DEFAULT_SEQUENCER_POSTING_TIMEFRAME` +
|
||||
/// `DEFAULT_SEQUENCER_POSTING_TIMEOUT` slots) plus normal confirmation lag.
|
||||
const COMMITTEE_SUBMISSION_COOLDOWN: Duration = Duration::from_secs(20);
|
||||
/// Channel slots between committee-config submissions; a margin over
|
||||
/// observed Bedrock confirmation lag.
|
||||
const COMMITTEE_SUBMISSION_COOLDOWN: SlotCount = 10;
|
||||
|
||||
/// Starts the sequencer using the provided configuration.
|
||||
/// If an existing database is found, the sequencer state is loaded from it and
|
||||
@@ -419,7 +415,7 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
||||
sequencer_config: config,
|
||||
block_publisher,
|
||||
watchers,
|
||||
last_committee_submission_at: None,
|
||||
last_committee_submission_slot: None,
|
||||
};
|
||||
|
||||
sequencer_core_metrics::record_chain_height(sequencer_core.chain_height());
|
||||
@@ -728,6 +724,19 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the channel has advanced far enough past `last_submission` to
|
||||
/// submit again. A missing tip counts as no advance.
|
||||
fn committee_cooldown_elapsed(last_submission: Option<Slot>, tip: Option<Slot>) -> bool {
|
||||
let Some(last_submission) = last_submission else {
|
||||
return true;
|
||||
};
|
||||
tip.is_some_and(|tip| {
|
||||
tip.into_inner()
|
||||
.saturating_sub(last_submission.into_inner())
|
||||
>= Self::COMMITTEE_SUBMISSION_COOLDOWN
|
||||
})
|
||||
}
|
||||
|
||||
async fn submit_committee_update(
|
||||
&mut self,
|
||||
committee_update: Option<Vec<sequencer_stake_core::SequencerKey>>,
|
||||
@@ -735,10 +744,14 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
||||
let Some(new_keys) = committee_update else {
|
||||
return;
|
||||
};
|
||||
if self
|
||||
.last_committee_submission_at
|
||||
.is_some_and(|at| at.elapsed() < Self::COMMITTEE_SUBMISSION_COOLDOWN)
|
||||
{
|
||||
let tip_slot = match self.block_publisher.channel_tip_slot().await {
|
||||
Ok(tip_slot) => tip_slot,
|
||||
Err(err) => {
|
||||
warn!("Failed to read channel tip slot; skipping committee update: {err:#}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
if !Self::committee_cooldown_elapsed(self.last_committee_submission_slot, tip_slot) {
|
||||
return;
|
||||
}
|
||||
let new_keys = new_keys
|
||||
@@ -748,7 +761,7 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
||||
.expect("sequencer key was decoded from a valid Ed25519 public key")
|
||||
})
|
||||
.collect();
|
||||
self.last_committee_submission_at = Some(Instant::now());
|
||||
self.last_committee_submission_slot = tip_slot;
|
||||
if let Err(err) = self.block_publisher.submit_channel_config(new_keys).await {
|
||||
warn!("Failed to submit committee channel-config update: {err:#}");
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use logos_blockchain_core::{
|
||||
},
|
||||
};
|
||||
use logos_blockchain_key_management_system_service::keys::{Ed25519Key, ZkPublicKey};
|
||||
use logos_blockchain_zone_sdk::sequencer::DepositInfo;
|
||||
use logos_blockchain_zone_sdk::{Slot, sequencer::DepositInfo};
|
||||
use mempool::MemPoolHandle;
|
||||
use ping_core::{ReceiverInstruction, ping_record_pda, receiver_config_account_id};
|
||||
use storage::sequencer::sequencer_cells::{
|
||||
@@ -135,6 +135,24 @@ fn only_the_cross_zone_inbox_is_sequencer_only() {
|
||||
assert!(!is_sequencer_only_program(programs::clock().id()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn committee_cooldown_needs_the_channel_to_advance() {
|
||||
type Core = SequencerCoreWithMockClients;
|
||||
let cooldown = Core::COMMITTEE_SUBMISSION_COOLDOWN;
|
||||
let submitted_at = Slot::new(100);
|
||||
|
||||
assert!(Core::committee_cooldown_elapsed(None, None));
|
||||
assert!(!Core::committee_cooldown_elapsed(Some(submitted_at), None));
|
||||
assert!(!Core::committee_cooldown_elapsed(
|
||||
Some(submitted_at),
|
||||
Some(Slot::new(100 + cooldown - 1))
|
||||
));
|
||||
assert!(Core::committee_cooldown_elapsed(
|
||||
Some(submitted_at),
|
||||
Some(Slot::new(100 + cooldown))
|
||||
));
|
||||
}
|
||||
|
||||
fn create_signing_key_for_account1() -> lee::PrivateKey {
|
||||
initial_pub_accounts_private_keys()[0].pub_sign_key.clone()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user