Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify slashes queue processing #800

Merged
merged 3 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 64 additions & 68 deletions pallets/external-validator-slashes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ use {
offence::{OffenceDetails, OnOffenceHandler},
EraIndex, SessionIndex,
},
sp_std::collections::vec_deque::VecDeque,
sp_std::vec,
sp_std::vec::Vec,
tp_traits::{EraIndexProvider, InvulnerablesProvider, OnEraStart},
Expand Down Expand Up @@ -138,6 +139,9 @@ pub mod pallet {
/// Provider to retrieve the current block timestamp.
type TimestampProvider: Get<u64>;

/// How many queued slashes are being processed per block.
type QueuedSlashesProcessedPerBlock: Get<u32>;

/// The weight information of this pallet.
type WeightInfo: WeightInfo;
}
Expand Down Expand Up @@ -197,8 +201,8 @@ pub mod pallet {
#[pallet::storage]
#[pallet::unbounded]
#[pallet::getter(fn unreported_slashes)]
pub type UnreportedSlashes<T: Config> =
StorageValue<_, Vec<Slash<T::AccountId, T::SlashId>>, ValueQuery>;
pub type UnreportedSlashesQueue<T: Config> =
StorageValue<_, VecDeque<Slash<T::AccountId, T::SlashId>>, ValueQuery>;

#[pallet::call]
impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -339,6 +343,19 @@ pub mod pallet {
Ok(())
}
}

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
let weight = Weight::zero();

Self::process_slashes_queue_page();

// TODO: Weight

weight
}
}
}

/// This is intended to be used with `FilterHistoricalOffences`.
Expand Down Expand Up @@ -494,87 +511,66 @@ impl<T: Config> OnEraStart for Pallet<T> {
}
});

Self::confirm_unconfirmed_slashes(era_index);
Self::add_era_slashes_to_queue(era_index);
}
}

impl<T: Config> Pallet<T> {
/// Apply previously-unapplied slashes on the beginning of a new era, after a delay.
/// In this case, we also send (or schedule for sending) slashes to ethereum
fn confirm_unconfirmed_slashes(active_era: EraIndex) {
const SLASH_PAGE_SIZE: usize = 20;
fn add_era_slashes_to_queue(active_era: EraIndex) {
let mut slashes: VecDeque<_> = Slashes::<T>::take(&active_era).into();

Slashes::<T>::mutate(&active_era, |era_slashes| {
let unreported_slashes = UnreportedSlashes::<T>::get();
UnreportedSlashesQueue::<T>::mutate(|queue| queue.append(&mut slashes));
}

let free_slashing_space = SLASH_PAGE_SIZE.saturating_sub(unreported_slashes.len());
fn process_slashes_queue_page() {
let mut slashes_to_send: Vec<_> = vec![];
let era_index = T::EraIndexProvider::active_era().index;

let mut slashes_to_send: Vec<_> = vec![];
// prepare up to QueuedSlashesProcessedPerBlock slashes to be sent
for _ in 0..(T::QueuedSlashesProcessedPerBlock::get() as usize) {
let Some(slash) = UnreportedSlashesQueue::<T>::mutate(VecDeque::pop_front) else {
// no more slashes to process in the queue
break;
};

for unreported_slash in unreported_slashes.iter() {
// TODO: check if validator.clone().encode() matches with the actual account bytes.
slashes_to_send.push((
unreported_slash.validator.clone().encode(),
unreported_slash.percentage.deconstruct(),
));
}
// TODO: check if validator.clone().encode() matches with the actual account bytes.
slashes_to_send.push((
slash.validator.clone().encode(),
slash.percentage.deconstruct(),
));
}

if slashes_to_send.is_empty() {
return;
}

// TODO: optimize code logic
if era_slashes.len() > free_slashing_space {
let limit = era_slashes.len().saturating_div(free_slashing_space);
// Build command with slashes.
let command = Command::ReportSlashes {
// TODO: change this
timestamp: T::TimestampProvider::get(),
era_index,
slashes: slashes_to_send,
};

let (slashes_to_include_send, slashes_to_unreport) = era_slashes.split_at(limit);
let channel_id: ChannelId = snowbridge_core::PRIMARY_GOVERNANCE_CHANNEL;

for slash_to_include in slashes_to_include_send.iter() {
slashes_to_send.push((
slash_to_include.validator.clone().encode(),
slash_to_include.percentage.deconstruct(),
));
}
//print!("Unreported slashes appending {:?}", slashes_to_unreport);
let outbound_message = Message {
id: None,
channel_id,
command,
};

UnreportedSlashes::<T>::mutate(|unreported_slashes| {
unreported_slashes.append(&mut slashes_to_unreport.to_vec());
});
} else {
for slash in era_slashes {
slash.confirmed = true;
slashes_to_send.push((
slash.validator.clone().encode(),
slash.percentage.deconstruct(),
));
// Validate and deliver the message
match T::ValidateMessage::validate(&outbound_message) {
Ok((ticket, _fee)) => {
if let Err(err) = T::OutboundQueue::deliver(ticket) {
log::error!(target: "ext_validators_slashes", "OutboundQueue delivery of message failed. {err:?}");
}
}

if slashes_to_send.len() > 0 {
let command = Command::ReportSlashes {
// TODO: change this
timestamp: T::TimestampProvider::get(),
era_index: active_era,
slashes: slashes_to_send,
};

let channel_id: ChannelId = snowbridge_core::PRIMARY_GOVERNANCE_CHANNEL;

let outbound_message = Message {
id: None,
channel_id,
command,
};

// Validate and deliver the message
match T::ValidateMessage::validate(&outbound_message) {
Ok((ticket, _fee)) => {
if let Err(err) = T::OutboundQueue::deliver(ticket) {
log::error!(target: "ext_validators_slashes", "OutboundQueue delivery of message failed. {err:?}");
}
}
Err(err) => {
log::error!(target: "ext_validators_slashes", "OutboundQueue validation of message failed. {err:?}");
}
};
Err(err) => {
log::error!(target: "ext_validators_slashes", "OutboundQueue validation of message failed. {err:?}");
}
});
};
}
}

Expand Down
3 changes: 2 additions & 1 deletion pallets/external-validator-slashes/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use {
crate as external_validator_slashes,
frame_support::{
parameter_types,
traits::{ConstU16, ConstU64, Get, OnInitialize, OnFinalize},
traits::{ConstU16, ConstU32, ConstU64, Get, Hooks},
},
frame_system as system,
snowbridge_core::outbound::{SendError, SendMessageFeeProvider},
Expand Down Expand Up @@ -255,6 +255,7 @@ impl external_validator_slashes::Config for Test {
type ValidateMessage = ();
type OutboundQueue = MockOkOutboundQueue;
type TimestampProvider = TimestampProvider;
type QueuedSlashesProcessedPerBlock = ConstU32<20>;
type WeightInfo = ();
}

Expand Down
17 changes: 8 additions & 9 deletions pallets/external-validator-slashes/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ fn test_on_offence_defer_period_0() {
}]
);
start_era(2, 2);
// One on-initialize should be needed to dispatch messages
roll_one_block();

assert_eq!(sent_ethereum_message_nonce(), 1);
Expand Down Expand Up @@ -300,16 +299,16 @@ fn test_on_offence_defer_period_0_messages_get_queued() {

assert_eq!(Slashes::<Test>::get(get_slashing_era(1)).len(), 25);
start_era(2, 2);
assert_eq!(UnreportedSlashes::<Test>::get().len(), 25);
assert_eq!(UnreportedSlashesQueue::<Test>::get().len(), 25);

// this triggers on_initialize
roll_one_block();
assert_eq!(sent_ethereum_message_nonce(), 1);
assert_eq!(UnreportedSlashes::<Test>::get().len(), 5);
assert_eq!(UnreportedSlashesQueue::<Test>::get().len(), 5);

roll_one_block();
assert_eq!(sent_ethereum_message_nonce(), 2);
assert_eq!(UnreportedSlashes::<Test>::get().len(), 0);
assert_eq!(UnreportedSlashesQueue::<Test>::get().len(), 0);
});
}

Expand All @@ -333,12 +332,12 @@ fn test_on_offence_defer_period_0_messages_get_queued_across_eras() {
}
assert_eq!(Slashes::<Test>::get(get_slashing_era(1)).len(), 25);
start_era(2, 2);
assert_eq!(UnreportedSlashes::<Test>::get().len(), 25);
assert_eq!(UnreportedSlashesQueue::<Test>::get().len(), 25);

// this triggers on_initialize
roll_one_block();
assert_eq!(sent_ethereum_message_nonce(), 1);
assert_eq!(UnreportedSlashes::<Test>::get().len(), 5);
assert_eq!(UnreportedSlashesQueue::<Test>::get().len(), 5);

// We have 5 non-dispatched, which should accumulate
// We shoulld have 30 after we initialie era 3
Expand All @@ -356,16 +355,16 @@ fn test_on_offence_defer_period_0_messages_get_queued_across_eras() {
}

start_era(3, 3);
assert_eq!(UnreportedSlashes::<Test>::get().len(), 30);
assert_eq!(UnreportedSlashesQueue::<Test>::get().len(), 30);

// this triggers on_initialize
roll_one_block();
assert_eq!(UnreportedSlashes::<Test>::get().len(), 10);
assert_eq!(UnreportedSlashesQueue::<Test>::get().len(), 10);
assert_eq!(sent_ethereum_message_nonce(), 2);

// this triggers on_initialize
roll_one_block();
assert_eq!(UnreportedSlashes::<Test>::get().len(), 0);
assert_eq!(UnreportedSlashesQueue::<Test>::get().len(), 0);
assert_eq!(sent_ethereum_message_nonce(), 3);

});
Expand Down
1 change: 1 addition & 0 deletions solo-chains/runtime/dancelight/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,7 @@ impl pallet_external_validator_slashes::Config for Runtime {
type ValidateMessage = tp_bridge::MessageValidator<Runtime>;
type OutboundQueue = tp_bridge::CustomSendMessage<Runtime, GetAggregateMessageOriginTanssi>;
type TimestampProvider = TimestampProvider;
type QueuedSlashesProcessedPerBlock = ConstU32<20>;
type WeightInfo = weights::pallet_external_validator_slashes::SubstrateWeight<Runtime>;
}

Expand Down
Loading