1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
use {
18
    crate::{
19
        tests::common::*, BondingDuration, EthereumSystem, ExternalValidatorSlashes,
20
        ExternalValidators, Grandpa, Historical, RuntimeEvent, SessionsPerEra, SlashDeferDuration,
21
        UseSnowbridgeV2,
22
    },
23
    alloc::vec,
24
    frame_support::{assert_noop, assert_ok, traits::KeyOwnerProofSystem},
25
    parity_scale_codec::Encode,
26
    sp_core::{Pair, H256},
27
    sp_runtime::Perbill,
28
    tp_bridge::Command,
29
    xcm::{latest::prelude::*, VersionedLocation},
30
};
31

            
32
#[test]
33
1
fn invulnerables_cannot_be_slashed_with_babe() {
34
1
    ExtBuilder::default()
35
1
        .with_balances(vec![
36
1
            // Alice gets 10k extra tokens for her mapping deposit
37
1
            (AccountId::from(ALICE), 210_000 * UNIT),
38
1
            (AccountId::from(BOB), 100_000 * UNIT),
39
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
40
1
            (AccountId::from(DAVE), 100_000 * UNIT),
41
1
        ])
42
1
        .build()
43
1
        .execute_with(|| {
44
1
            run_to_block(2);
45
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
46
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
47
1
            assert_eq!(reports.len(), 1);
48
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
49

            
50
1
            let slashes = ExternalValidatorSlashes::slashes(
51
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1,
52
            );
53
1
            assert_eq!(slashes.len(), 0);
54
1
        });
55
1
}
56

            
57
#[test]
58
1
fn invulnerables_cannot_be_slashed_with_grandpa() {
59
1
    ExtBuilder::default()
60
1
        .with_balances(vec![
61
1
            // Alice gets 10k extra tokens for her mapping deposit
62
1
            (AccountId::from(ALICE), 210_000 * UNIT),
63
1
            (AccountId::from(BOB), 100_000 * UNIT),
64
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
65
1
            (AccountId::from(DAVE), 100_000 * UNIT),
66
1
        ])
67
1
        .build()
68
1
        .execute_with(|| {
69
1
            run_to_block(2);
70
1
            inject_grandpa_slash(&AccountId::from(ALICE).to_string());
71
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
72
1
            assert_eq!(reports.len(), 1);
73
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
74

            
75
1
            let slashes = ExternalValidatorSlashes::slashes(
76
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1,
77
            );
78
1
            assert_eq!(slashes.len(), 0);
79
1
        });
80
1
}
81

            
82
#[test]
83
1
fn non_invulnerables_can_be_slashed_with_babe() {
84
1
    ExtBuilder::default()
85
1
        .with_balances(vec![
86
1
            // Alice gets 10k extra tokens for her mapping deposit
87
1
            (AccountId::from(ALICE), 210_000 * UNIT),
88
1
            (AccountId::from(BOB), 100_000 * UNIT),
89
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
90
1
            (AccountId::from(DAVE), 100_000 * UNIT),
91
1
        ])
92
1
        .build()
93
1
        .execute_with(|| {
94
1
            run_to_block(2);
95
1
            assert_ok!(ExternalValidators::remove_whitelisted(
96
1
                RuntimeOrigin::root(),
97
1
                AccountId::from(ALICE)
98
            ));
99

            
100
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
101

            
102
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
103
1
            assert_eq!(reports.len(), 1);
104
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
105

            
106
1
            let slashes = ExternalValidatorSlashes::slashes(
107
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1,
108
            );
109
1
            assert_eq!(slashes.len(), 1);
110
1
            assert_eq!(slashes[0].validator, AccountId::from(ALICE));
111
            //the formula is (3*offenders/num_validators)^2
112
            // since we have 1 offender, 2 validators, this makes it a maximum of 1
113
1
            assert_eq!(slashes[0].percentage, Perbill::from_percent(100));
114
1
        });
115
1
}
116

            
117
#[test]
118
1
fn non_invulnerables_can_be_slashed_with_grandpa() {
119
1
    ExtBuilder::default()
120
1
        .with_balances(vec![
121
1
            // Alice gets 10k extra tokens for her mapping deposit
122
1
            (AccountId::from(ALICE), 210_000 * UNIT),
123
1
            (AccountId::from(BOB), 100_000 * UNIT),
124
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
125
1
            (AccountId::from(DAVE), 100_000 * UNIT),
126
1
        ])
127
1
        .build()
128
1
        .execute_with(|| {
129
1
            run_to_block(2);
130
1
            assert_ok!(ExternalValidators::remove_whitelisted(
131
1
                RuntimeOrigin::root(),
132
1
                AccountId::from(ALICE)
133
            ));
134

            
135
1
            inject_grandpa_slash(&AccountId::from(ALICE).to_string());
136

            
137
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
138
1
            assert_eq!(reports.len(), 1);
139
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
140

            
141
1
            let slashes = ExternalValidatorSlashes::slashes(
142
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1,
143
            );
144
1
            assert_eq!(slashes.len(), 1);
145
1
            assert_eq!(slashes[0].validator, AccountId::from(ALICE));
146
            //the formula is (3*offenders/num_validators)^2
147
            // since we have 1 offender, 2 validators, this makes it a maximum of 1
148
1
            assert_eq!(slashes[0].percentage, Perbill::from_percent(100));
149
1
        });
150
1
}
151

            
152
#[test]
153
1
fn test_slashing_percentage_applied_correctly() {
154
1
    ExtBuilder::default()
155
1
        .with_balances(vec![
156
1
            // Alice gets 10k extra tokens for her mapping deposit
157
1
            (AccountId::from(ALICE), 210_000 * UNIT),
158
1
            (AccountId::from(BOB), 100_000 * UNIT),
159
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
160
1
            (AccountId::from(DAVE), 100_000 * UNIT),
161
1
        ])
162
1
        .with_validators(vec![
163
1
            (AccountId::from(ALICE), 210 * UNIT),
164
1
            (AccountId::from(BOB), 100 * UNIT),
165
1
            (AccountId::from(CHARLIE), 100 * UNIT),
166
1
            (AccountId::from(DAVE), 100 * UNIT),
167
1
        ])
168
1
        .build()
169
1
        .execute_with(|| {
170
1
            run_to_block(2);
171
1
            assert_ok!(ExternalValidators::remove_whitelisted(
172
1
                RuntimeOrigin::root(),
173
1
                AccountId::from(ALICE)
174
            ));
175

            
176
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
177

            
178
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
179
1
            assert_eq!(reports.len(), 1);
180
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
181

            
182
1
            let slashes = ExternalValidatorSlashes::slashes(
183
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1,
184
            );
185
1
            assert_eq!(slashes.len(), 1);
186
1
            assert_eq!(slashes[0].validator, AccountId::from(ALICE));
187
            //the formula is (3*offenders/num_validators)^2
188
            // since we have 1 offender, 4 validators, this makes it a maximum of 0.75^2=0.5625
189
1
            assert_eq!(slashes[0].percentage, Perbill::from_parts(562500000));
190
1
        });
191
1
}
192

            
193
#[test]
194
1
fn test_slashes_are_not_additive_in_percentage() {
195
1
    ExtBuilder::default()
196
1
        .with_balances(vec![
197
1
            // Alice gets 10k extra tokens for her mapping deposit
198
1
            (AccountId::from(ALICE), 210_000 * UNIT),
199
1
            (AccountId::from(BOB), 100_000 * UNIT),
200
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
201
1
            (AccountId::from(DAVE), 100_000 * UNIT),
202
1
            (AccountId::from(EVE), 100_000 * UNIT),
203
1
        ])
204
1
        .with_validators(vec![
205
1
            (AccountId::from(ALICE), 210 * UNIT),
206
1
            (AccountId::from(BOB), 100 * UNIT),
207
1
            (AccountId::from(CHARLIE), 100 * UNIT),
208
1
            (AccountId::from(DAVE), 100 * UNIT),
209
1
            (AccountId::from(EVE), 100 * UNIT),
210
1
        ])
211
1
        .build()
212
1
        .execute_with(|| {
213
1
            run_to_block(2);
214
1
            assert_ok!(ExternalValidators::remove_whitelisted(
215
1
                RuntimeOrigin::root(),
216
1
                AccountId::from(ALICE)
217
            ));
218

            
219
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
220

            
221
1
            inject_grandpa_slash(&AccountId::from(ALICE).to_string());
222

            
223
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
224

            
225
            // we have 2 reports
226
1
            assert_eq!(reports.len(), 2);
227
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
228

            
229
1
            let slashes = ExternalValidatorSlashes::slashes(
230
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1,
231
            );
232

            
233
            // but a single slash
234
1
            assert_eq!(slashes.len(), 1);
235
1
            assert_eq!(slashes[0].validator, AccountId::from(ALICE));
236
            // the formula is (3*offenders/num_validators)^2
237
            // since we have 1 offender, 5 validators, this makes it 0.36
238
            // we injected 2 offences BUT THEY ARE NOT ADDITIVE
239
1
            assert_eq!(slashes[0].percentage, Perbill::from_parts(360000000));
240
1
        });
241
1
}
242
#[test]
243
1
fn test_slashes_are_cleaned_after_bonding_period() {
244
1
    ExtBuilder::default()
245
1
        .with_balances(vec![
246
1
            // Alice gets 10k extra tokens for her mapping deposit
247
1
            (AccountId::from(ALICE), 210_000 * UNIT),
248
1
            (AccountId::from(BOB), 100_000 * UNIT),
249
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
250
1
            (AccountId::from(DAVE), 100_000 * UNIT),
251
1
        ])
252
1
        .build()
253
1
        .execute_with(|| {
254
1
            run_to_block(2);
255
1
            assert_ok!(ExternalValidators::remove_whitelisted(
256
1
                RuntimeOrigin::root(),
257
1
                AccountId::from(ALICE)
258
            ));
259

            
260
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
261

            
262
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
263
1
            assert_eq!(reports.len(), 1);
264
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
265

            
266
1
            let slashes = ExternalValidatorSlashes::slashes(
267
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1,
268
            );
269
1
            assert_eq!(slashes.len(), 1);
270
            // The first session in which the era 3 will be pruned is
271
            // (28+3+1)*sessionsPerEra
272
1
            let first_session_era_3_pruned = (ExternalValidators::current_era().unwrap()
273
1
                + SlashDeferDuration::get()
274
1
                + 1
275
1
                + BondingDuration::get()
276
1
                + 1)
277
1
                * SessionsPerEra::get();
278

            
279
1
            let first_era_deferred =
280
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
281

            
282
1
            println!("first era deferred {:?}", first_era_deferred);
283
1
            println!("first_session_era_3_pruned {:?}", first_session_era_3_pruned);
284
1
            if first_session_era_3_pruned > 20 {
285
                panic!("Test will take too long, aborting. Make sure to compile with --features fast-runtime");
286
1
            }
287
1
            run_to_session(first_session_era_3_pruned);
288

            
289
1
            let slashes_after_bonding_period =
290
1
                ExternalValidatorSlashes::slashes(first_era_deferred);
291
1
            assert_eq!(slashes_after_bonding_period.len(), 0);
292
1
        });
293
1
}
294

            
295
#[test]
296
1
fn test_slashes_can_be_cleared_before_deferred_period_applies() {
297
1
    ExtBuilder::default()
298
1
        .with_balances(vec![
299
1
            // Alice gets 10k extra tokens for her mapping deposit
300
1
            (AccountId::from(ALICE), 210_000 * UNIT),
301
1
            (AccountId::from(BOB), 100_000 * UNIT),
302
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
303
1
            (AccountId::from(DAVE), 100_000 * UNIT),
304
1
        ])
305
1
        .build()
306
1
        .execute_with(|| {
307
1
            run_to_block(2);
308
1
            assert_ok!(ExternalValidators::remove_whitelisted(
309
1
                RuntimeOrigin::root(),
310
1
                AccountId::from(ALICE)
311
            ));
312

            
313
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
314

            
315
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
316
1
            assert_eq!(reports.len(), 1);
317
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
318

            
319
1
            let deferred_era =
320
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
321
1
            let slashes = ExternalValidatorSlashes::slashes(deferred_era);
322
1
            assert_eq!(slashes.len(), 1);
323
1
            assert_eq!(slashes[0].validator, AccountId::from(ALICE));
324

            
325
            // Now let's clean it up
326
1
            assert_ok!(ExternalValidatorSlashes::cancel_deferred_slash(
327
1
                RuntimeOrigin::root(),
328
1
                deferred_era,
329
1
                vec![0]
330
            ));
331
1
            let slashes_after_cancel = ExternalValidatorSlashes::slashes(deferred_era);
332
1
            assert_eq!(slashes_after_cancel.len(), 0);
333
1
        });
334
1
}
335

            
336
#[test]
337
1
fn test_slashes_cannot_be_cancelled_after_defer_period() {
338
1
    ExtBuilder::default()
339
1
        .with_balances(vec![
340
1
            // Alice gets 10k extra tokens for her mapping deposit
341
1
            (AccountId::from(ALICE), 210_000 * UNIT),
342
1
            (AccountId::from(BOB), 100_000 * UNIT),
343
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
344
1
            (AccountId::from(DAVE), 100_000 * UNIT),
345
1
        ])
346
1
        .build()
347
1
        .execute_with(|| {
348
1
            let token_location: VersionedLocation = Location::here().into();
349

            
350
1
            assert_ok!(EthereumSystem::register_token(
351
1
                root_origin(),
352
1
                Box::new(token_location),
353
1
                snowbridge_core::AssetMetadata {
354
1
                    name: "dance".as_bytes().to_vec().try_into().unwrap(),
355
1
                    symbol: "dance".as_bytes().to_vec().try_into().unwrap(),
356
1
                    decimals: 12,
357
1
                }
358
            ));
359

            
360
1
            run_to_block(2);
361
1
            assert_ok!(ExternalValidators::remove_whitelisted(
362
1
                RuntimeOrigin::root(),
363
1
                AccountId::from(ALICE)
364
            ));
365

            
366
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
367

            
368
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
369
1
            assert_eq!(reports.len(), 1);
370
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
371

            
372
1
            let deferred_era =
373
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
374

            
375
1
            let slashes = ExternalValidatorSlashes::slashes(deferred_era);
376
1
            assert_eq!(slashes.len(), 1);
377
1
            assert_eq!(slashes[0].validator, AccountId::from(ALICE));
378

            
379
            // The first session in which the era 3 will be deferred is 18
380
            // 3 sessions per era
381
            // (externalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1)*SessionsPerEra
382
            // formula is:
383

            
384
1
            let first_deferred_session =
385
1
                (ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1)
386
1
                    * SessionsPerEra::get();
387
1
            run_to_session(first_deferred_session);
388

            
389
1
            assert_eq!(ExternalValidators::current_era().unwrap(), deferred_era);
390
            // Now let's clean it up
391
1
            assert_noop!(
392
1
                ExternalValidatorSlashes::cancel_deferred_slash(
393
1
                    RuntimeOrigin::root(),
394
1
                    deferred_era,
395
1
                    vec![0]
396
                ),
397
1
                pallet_external_validator_slashes::Error::<crate::Runtime>::DeferPeriodIsOver
398
            );
399
1
        });
400
1
}
401

            
402
#[test]
403
1
fn test_slashes_are_sent_to_ethereum() {
404
1
    sp_tracing::try_init_simple();
405
1
    ExtBuilder::default()
406
1
        .with_balances(vec![
407
1
            // Alice gets 10k extra tokens for her mapping deposit
408
1
            (AccountId::from(ALICE), 210_000 * UNIT),
409
1
            (AccountId::from(BOB), 100_000 * UNIT),
410
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
411
1
            (AccountId::from(DAVE), 100_000 * UNIT),
412
1
        ])
413
1
        .with_validators(vec![])
414
1
        .with_external_validators(vec![
415
1
            (AccountId::from(ALICE), 210 * UNIT),
416
1
            (AccountId::from(BOB), 100 * UNIT),
417
1
        ])
418
1
        .build()
419
1
        .execute_with(|| {
420
1
            let token_location: VersionedLocation = Location::here().into();
421

            
422
1
            assert_ok!(EthereumSystem::register_token(
423
1
                root_origin(),
424
1
                Box::new(token_location),
425
1
                snowbridge_core::AssetMetadata {
426
1
                    name: "dance".as_bytes().to_vec().try_into().unwrap(),
427
1
                    symbol: "dance".as_bytes().to_vec().try_into().unwrap(),
428
1
                    decimals: 12,
429
1
                }
430
            ));
431

            
432
1
            run_to_block(2);
433

            
434
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
435

            
436
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
437
1
            assert_eq!(reports.len(), 1);
438
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
439

            
440
1
            let deferred_era =
441
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
442

            
443
1
            let slashes = ExternalValidatorSlashes::slashes(deferred_era);
444
1
            assert_eq!(slashes.len(), 1);
445
1
            assert_eq!(slashes[0].validator, AccountId::from(ALICE));
446

            
447
1
            let session_in_which_slashes_are_sent =
448
1
                (ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1)
449
1
                    * SessionsPerEra::get();
450
1
            run_to_session(session_in_which_slashes_are_sent);
451

            
452
1
            let outbound_msg_queue_event = System::events()
453
1
                .iter()
454
11
                .filter(|r| {
455
10
                    matches!(
456
2
                        r.event,
457
                        RuntimeEvent::EthereumOutboundQueue(
458
                            snowbridge_pallet_outbound_queue::Event::MessageQueued { .. },
459
                        )
460
                    )
461
11
                })
462
1
                .count();
463

            
464
            // We have two reasons for sending messages:
465
            // 1, because on_era_end sends rewards
466
            // 2, because on_era_start sends slashes
467
            // Both session ends and session starts are done on_initialize of frame-sesssion
468
1
            assert_eq!(
469
                outbound_msg_queue_event, 1,
470
                "MessageQueued event should be emitted"
471
            );
472

            
473
            // Slashes start being sent after the era block
474
            // They are scheduled as unprocessedSlashes
475
1
            run_block();
476

            
477
1
            let outbound_msg_queue_event = System::events()
478
1
                .iter()
479
4
                .filter(|r| {
480
3
                    matches!(
481
2
                        r.event,
482
                        RuntimeEvent::EthereumOutboundQueue(
483
                            snowbridge_pallet_outbound_queue::Event::MessageQueued { .. },
484
                        )
485
                    )
486
4
                })
487
1
                .count();
488

            
489
1
            let mut slashes_command_found: Option<Command> = None;
490
1
            let mut message_id_found: Option<H256> = None;
491
1
            let ext_validators_slashes_event = System::events()
492
1
                .iter()
493
1
                .filter(|r| match &r.event {
494
                    RuntimeEvent::ExternalValidatorSlashes(
495
                        pallet_external_validator_slashes::Event::SlashesMessageSent {
496
1
                            slashes_command,
497
1
                            message_id,
498
                        },
499
                    ) => {
500
1
                        message_id_found = Some(*message_id);
501
1
                        slashes_command_found = Some(slashes_command.clone());
502
1
                        true
503
                    }
504
3
                    _ => false,
505
4
                })
506
1
                .count();
507

            
508
            // This one is related to slashes
509
1
            assert_eq!(
510
                outbound_msg_queue_event, 1,
511
                "MessageQueued event should be emitted"
512
            );
513

            
514
1
            assert_eq!(
515
                ext_validators_slashes_event, 1,
516
                "SlashesMessageSent event should be emitted"
517
            );
518

            
519
1
            let expected_slashes = vec![SlashData {
520
1
                encoded_validator_id: AccountId::from(ALICE).encode(),
521
1
                slash_fraction: Perbill::from_percent(100).deconstruct(),
522
1
                external_idx: 0,
523
1
            }];
524

            
525
1
            let expected_slashes_command = Command::ReportSlashes {
526
1
                era_index: 1u32,
527
1
                slashes: expected_slashes,
528
1
            };
529

            
530
1
            assert_eq!(
531
1
                slashes_command_found.unwrap(),
532
                expected_slashes_command,
533
                "Both slashes commands should match!"
534
            );
535

            
536
1
            assert_eq!(message_id_found.unwrap(), read_last_entropy().into());
537

            
538
            // EthereumOutboundQueue -> queue_message -> MessageQQueuePallet (queue)
539
            // MessageQueuePallet on_initialize -> dispatch queue -> process_message -> EthereumOutboundQueue_process_message
540
1
            let nonce = snowbridge_pallet_outbound_queue::Nonce::<Runtime>::get(
541
                snowbridge_core::PRIMARY_GOVERNANCE_CHANNEL,
542
            );
543

            
544
            // We dispatched 2 already
545
1
            assert_eq!(nonce, 2);
546
1
        });
547
1
}
548

            
549
use {frame_support::traits::Get, tp_bridge::SlashData};
550

            
551
#[test]
552
1
fn test_slashes_are_sent_to_ethereum_accumulatedly() {
553
1
    sp_tracing::try_init_simple();
554
1
    ExtBuilder::default()
555
1
        .with_balances(vec![
556
1
            // Alice gets 10k extra tokens for her mapping deposit
557
1
            (AccountId::from(ALICE), 210_000 * UNIT),
558
1
            (AccountId::from(BOB), 100_000 * UNIT),
559
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
560
1
            (AccountId::from(DAVE), 100_000 * UNIT),
561
1
        ])
562
1
        .with_validators(
563
1
            vec![]
564
1
        )
565
1
        .with_external_validators(
566
1
            vec![
567
1
                (AccountId::from(ALICE), 210 * UNIT),
568
1
                (AccountId::from(BOB), 100 * UNIT),
569
1
            ]
570
1
        )
571
1
        .build()
572
1
        .execute_with(|| {
573
1
            let token_location: VersionedLocation = Location::here()
574
1
            .into();
575

            
576
1
            assert_ok!(EthereumSystem::register_token(root_origin(), Box::new(token_location), snowbridge_core::AssetMetadata {
577
1
                name: "dance".as_bytes().to_vec().try_into().unwrap(),
578
1
                symbol: "dance".as_bytes().to_vec().try_into().unwrap(),
579
1
                decimals: 12,
580
1
		    }));
581

            
582

            
583
1
            run_to_block(2);
584

            
585
            // We can inject arbitraqry slashes for arbitary accounts with root
586
1
            let page_limit: u32 = <Runtime as pallet_external_validator_slashes::Config>::QueuedSlashesProcessedPerBlock::get();
587
11
            for i in 0..page_limit +1 {
588
11
                assert_ok!(ExternalValidatorSlashes::force_inject_slash(
589
11
                    RuntimeOrigin::root(),
590
                    0,
591
11
                    AccountId::new(H256::from_low_u64_be(u64::from(i)).to_fixed_bytes()),
592
11
                    Perbill::from_percent(75),
593
                    1
594
                ));
595
            }
596

            
597
1
            let deferred_era = ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
598

            
599
1
            let slashes = ExternalValidatorSlashes::slashes(deferred_era);
600
1
            assert_eq!(slashes.len() as u32, page_limit +1);
601

            
602
1
            let session_in_which_slashes_are_sent =
603
1
                (ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1)
604
1
                    * SessionsPerEra::get();
605
1
            run_to_session(session_in_which_slashes_are_sent);
606

            
607
1
            let outbound_msg_queue_event = System::events()
608
1
                .iter()
609
2
                .filter(|r| matches!(r.event,
610
                    RuntimeEvent::EthereumOutboundQueue(
611
                        snowbridge_pallet_outbound_queue::Event::MessageQueued { .. },
612
                    )))
613
1
                .count();
614

            
615
            // We have two reasons for sending messages:
616
            // 1, because on_era_end sends rewards
617
            // 2, because on_era_start sends slashes
618
            // Both session ends and session starts are done on_initialize of frame-sesssion
619
1
            assert_eq!(
620
                outbound_msg_queue_event, 1,
621
                "MessageQueued event should be emitted"
622
            );
623

            
624
            // We still have all slashes as unprocessed
625
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
626
1
            assert_eq!(unprocessed_slashes.len() as u32, page_limit +1);
627

            
628

            
629
            // Slashes start being sent after the era block
630
            // They are scheduled as unprocessedSlashes
631
1
            run_block();
632

            
633
1
            let outbound_msg_queue_event = System::events()
634
1
                .iter()
635
2
                .filter(|r| matches!(r.event,
636
                    RuntimeEvent::EthereumOutboundQueue(
637
                        snowbridge_pallet_outbound_queue::Event::MessageQueued { .. },
638
                    )))
639
1
                .count();
640

            
641
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
642

            
643
            // This one is related to slashes
644
1
            assert_eq!(
645
                outbound_msg_queue_event, 1,
646
                "MessageQueued event should be emitted"
647
            );
648

            
649
            // We still should have one pending unprocessed slash, to be sent in the next block
650
1
            assert_eq!(unprocessed_slashes.len() as u32, 1);
651

            
652
1
            run_block();
653

            
654
1
            let outbound_msg_queue_event = System::events()
655
1
                .iter()
656
2
                .filter(|r| matches!(r.event,
657
                    RuntimeEvent::EthereumOutboundQueue(
658
                        snowbridge_pallet_outbound_queue::Event::MessageQueued { .. },
659
                    )
660
                ))
661
1
                .count();
662

            
663
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
664

            
665
            // This one is related to slashes
666
1
            assert_eq!(
667
                outbound_msg_queue_event, 1,
668
                "MessageQueued event should be emitted"
669
            );
670

            
671
            // Now we should have 0
672
1
            assert_eq!(unprocessed_slashes.len() as u32, 0);
673

            
674
            // EthereumOutboundQueue -> queue_message -> MessageQQueuePallet (queue)
675
            // MessageQueuePallet on_initialize -> dispatch queue -> process_message -> EthereumOutboundQueue_process_message
676
1
            let nonce = snowbridge_pallet_outbound_queue::Nonce::<Runtime>::get(
677
                snowbridge_core::PRIMARY_GOVERNANCE_CHANNEL,
678
            );
679

            
680
            // We dispatched 3 already
681
            // 1 reward + 2 slashes
682
1
            assert_eq!(nonce, 3);
683
1
        });
684
1
}
685

            
686
#[test]
687
1
fn test_slashes_are_sent_to_ethereum_accumulate_until_next_era() {
688
1
    sp_tracing::try_init_simple();
689
1
    ExtBuilder::default()
690
1
        .with_balances(vec![
691
1
            // Alice gets 10k extra tokens for her mapping deposit
692
1
            (AccountId::from(ALICE), 210_000 * UNIT),
693
1
            (AccountId::from(BOB), 100_000 * UNIT),
694
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
695
1
            (AccountId::from(DAVE), 100_000 * UNIT),
696
1
        ])
697
1
        .with_validators(
698
1
            vec![]
699
1
        )
700
1
        .with_external_validators(
701
1
            vec![
702
1
                (AccountId::from(ALICE), 210 * UNIT),
703
1
                (AccountId::from(BOB), 100 * UNIT),
704
1
            ]
705
1
        )
706
1
        .build()
707
1
        .execute_with(|| {
708
1
            let token_location: VersionedLocation = Location::here()
709
1
            .into();
710

            
711
1
            assert_ok!(EthereumSystem::register_token(root_origin(), Box::new(token_location), snowbridge_core::AssetMetadata {
712
1
                name: "dance".as_bytes().to_vec().try_into().unwrap(),
713
1
                symbol: "dance".as_bytes().to_vec().try_into().unwrap(),
714
1
                decimals: 12,
715
1
		    }));
716

            
717
1
            run_to_block(2);
718

            
719
            // We can inject arbitraqry slashes for arbitary accounts with root
720
1
            let page_limit: u32 = <Runtime as pallet_external_validator_slashes::Config>::QueuedSlashesProcessedPerBlock::get();
721

            
722
1
            let blocks_in_era = crate::EpochDurationInBlocks::get() * SessionsPerEra::get();
723
1
            let total_slashes_to_inject = blocks_in_era*page_limit +1;
724
301
            for i in 0..total_slashes_to_inject {
725
301
                assert_ok!(ExternalValidatorSlashes::force_inject_slash(
726
301
                    RuntimeOrigin::root(),
727
                    0,
728
301
                    AccountId::new(H256::from_low_u64_be(u64::from(i)).to_fixed_bytes()),
729
301
                    Perbill::from_percent(75),
730
                    1
731
                ));
732
            }
733

            
734
1
            let deferred_era = ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
735

            
736
1
            let slashes = ExternalValidatorSlashes::slashes(deferred_era);
737
1
            assert_eq!(slashes.len() as u32, total_slashes_to_inject);
738

            
739
1
            let session_in_which_slashes_are_sent =
740
1
                (ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1)
741
1
                    * SessionsPerEra::get();
742
1
            run_to_session(session_in_which_slashes_are_sent);
743

            
744
1
            let outbound_msg_queue_event = System::events()
745
1
                .iter()
746
2
                .filter(|r| matches!(r.event,
747
                    RuntimeEvent::EthereumOutboundQueue(
748
                        snowbridge_pallet_outbound_queue::Event::MessageQueued { .. },
749
                    )
750
                ))
751
1
                .count();
752

            
753
            // We have two reasons for sending messages:
754
            // 1, because on_era_end sends rewards
755
            // 2, because on_era_start sends slashes
756
            // Both session ends and session starts are done on_initialize of frame-sesssion
757
1
            assert_eq!(
758
                outbound_msg_queue_event, 1,
759
                "MessageQueued event should be emitted"
760
            );
761

            
762
            // We still have all slashes as unprocessed
763
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
764
1
            assert_eq!(unprocessed_slashes.len() as u32, total_slashes_to_inject);
765

            
766
            // Running to the next era, but we should still have unprocessed
767
1
            run_to_session((ExternalValidators::current_era().unwrap() +1)*SessionsPerEra::get());
768

            
769
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
770

            
771
            // We still should have one pending unprocessed slash, to be sent in the next block
772
1
            assert_eq!(unprocessed_slashes.len() as u32, 1);
773

            
774
            // And in this case, we have 2 events
775
            // the rewards one plus the one where we sent remaining slashes
776
1
            let outbound_msg_queue_event = System::events()
777
1
                .iter()
778
3
                .filter(|r| matches!(r.event,
779
                    RuntimeEvent::EthereumOutboundQueue(
780
                        snowbridge_pallet_outbound_queue::Event::MessageQueued { .. },
781
                    )
782
                ))
783
1
                .count();
784
1
            assert_eq!(
785
                outbound_msg_queue_event, 2,
786
                "MessageQueued event should be emitted"
787
            );
788
1
        });
789
1
}
790

            
791
#[test]
792
1
fn test_slashes_are_sent_to_ethereum_v2() {
793
1
    sp_tracing::try_init_simple();
794
1
    ExtBuilder::default()
795
1
        .with_balances(vec![
796
1
            // Alice gets 10k extra tokens for her mapping deposit
797
1
            (AccountId::from(ALICE), 210_000 * UNIT),
798
1
            (AccountId::from(BOB), 100_000 * UNIT),
799
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
800
1
            (AccountId::from(DAVE), 100_000 * UNIT),
801
1
        ])
802
1
        .with_validators(vec![])
803
1
        .with_external_validators(vec![
804
1
            (AccountId::from(ALICE), 210 * UNIT),
805
1
            (AccountId::from(BOB), 100 * UNIT),
806
1
        ])
807
1
        .build()
808
1
        .execute_with(|| {
809
1
            UseSnowbridgeV2::set(&true);
810
1
            let token_location: VersionedLocation = Location::here().into();
811

            
812
1
            assert_ok!(EthereumSystem::register_token(
813
1
                root_origin(),
814
1
                Box::new(token_location),
815
1
                snowbridge_core::AssetMetadata {
816
1
                    name: "dance".as_bytes().to_vec().try_into().unwrap(),
817
1
                    symbol: "dance".as_bytes().to_vec().try_into().unwrap(),
818
1
                    decimals: 12,
819
1
                }
820
            ));
821

            
822
1
            run_to_block(2);
823

            
824
1
            inject_babe_slash(&AccountId::from(ALICE).to_string());
825

            
826
1
            let reports = pallet_offences::Reports::<crate::Runtime>::iter().collect::<Vec<_>>();
827
1
            assert_eq!(reports.len(), 1);
828
1
            assert_eq!(ExternalValidators::current_era().unwrap(), 0);
829

            
830
1
            let deferred_era =
831
1
                ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
832

            
833
1
            let slashes = ExternalValidatorSlashes::slashes(deferred_era);
834
1
            assert_eq!(slashes.len(), 1);
835
1
            assert_eq!(slashes[0].validator, AccountId::from(ALICE));
836

            
837
1
            let session_in_which_slashes_are_sent =
838
1
                (ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1)
839
1
                    * SessionsPerEra::get();
840
1
            run_to_session(session_in_which_slashes_are_sent);
841

            
842
1
            let outbound_msg_queue_event = System::events()
843
1
                .iter()
844
11
                .filter(|r| {
845
10
                    matches!(
846
2
                        r.event,
847
                        RuntimeEvent::EthereumOutboundQueueV2(
848
                            snowbridge_pallet_outbound_queue_v2::Event::MessageQueued { .. },
849
                        )
850
                    )
851
11
                })
852
1
                .count();
853

            
854
            // We have two reasons for sending messages:
855
            // 1, because on_era_end sends rewards
856
            // 2, because on_era_start sends slashes
857
            // Both session ends and session starts are done on_initialize of frame-sesssion
858
1
            assert_eq!(
859
                outbound_msg_queue_event, 1,
860
                "MessageQueued event should be emitted"
861
            );
862

            
863
            // Slashes start being sent after the era block
864
            // They are scheduled as unprocessedSlashes
865
1
            run_block();
866

            
867
1
            let outbound_msg_queue_event = System::events()
868
1
                .iter()
869
4
                .filter(|r| {
870
3
                    matches!(
871
2
                        r.event,
872
                        RuntimeEvent::EthereumOutboundQueueV2(
873
                            snowbridge_pallet_outbound_queue_v2::Event::MessageQueued { .. },
874
                        )
875
                    )
876
4
                })
877
1
                .count();
878

            
879
1
            let mut slashes_command_found: Option<Command> = None;
880
1
            let mut message_id_found: Option<H256> = None;
881
1
            let ext_validators_slashes_event = System::events()
882
1
                .iter()
883
1
                .filter(|r| match &r.event {
884
                    RuntimeEvent::ExternalValidatorSlashes(
885
                        pallet_external_validator_slashes::Event::SlashesMessageSent {
886
1
                            slashes_command,
887
1
                            message_id,
888
                        },
889
                    ) => {
890
1
                        message_id_found = Some(*message_id);
891
1
                        slashes_command_found = Some(slashes_command.clone());
892
1
                        true
893
                    }
894
3
                    _ => false,
895
4
                })
896
1
                .count();
897

            
898
            // This one is related to slashes
899
1
            assert_eq!(
900
                outbound_msg_queue_event, 1,
901
                "MessageQueued event should be emitted"
902
            );
903

            
904
1
            assert_eq!(
905
                ext_validators_slashes_event, 1,
906
                "SlashesMessageSent event should be emitted"
907
            );
908

            
909
1
            let expected_slashes = vec![SlashData {
910
1
                encoded_validator_id: AccountId::from(ALICE).encode(),
911
1
                slash_fraction: Perbill::from_percent(100).deconstruct(),
912
1
                external_idx: 0,
913
1
            }];
914

            
915
1
            let expected_slashes_command = Command::ReportSlashes {
916
1
                era_index: 1u32,
917
1
                slashes: expected_slashes,
918
1
            };
919

            
920
1
            assert_eq!(
921
1
                slashes_command_found.unwrap(),
922
                expected_slashes_command,
923
                "Both slashes commands should match!"
924
            );
925

            
926
1
            assert_eq!(message_id_found.unwrap(), read_last_entropy().into());
927

            
928
            // EthereumOutboundQueue -> queue_message -> MessageQQueuePallet (queue)
929
            // MessageQueuePallet on_initialize -> dispatch queue -> process_message -> EthereumOutboundQueue_process_message
930
1
            let nonce = snowbridge_pallet_outbound_queue_v2::Nonce::<Runtime>::get();
931

            
932
            // We dispatched 2 already
933
1
            assert_eq!(nonce, 2);
934
1
        });
935
1
}
936

            
937
#[test]
938
1
fn test_slashes_are_sent_to_ethereum_accumulatedly_v2() {
939
1
    sp_tracing::try_init_simple();
940
1
    ExtBuilder::default()
941
1
        .with_balances(vec![
942
1
            // Alice gets 10k extra tokens for her mapping deposit
943
1
            (AccountId::from(ALICE), 210_000 * UNIT),
944
1
            (AccountId::from(BOB), 100_000 * UNIT),
945
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
946
1
            (AccountId::from(DAVE), 100_000 * UNIT),
947
1
        ])
948
1
        .with_validators(
949
1
            vec![]
950
1
        )
951
1
        .with_external_validators(
952
1
            vec![
953
1
                (AccountId::from(ALICE), 210 * UNIT),
954
1
                (AccountId::from(BOB), 100 * UNIT),
955
1
            ]
956
1
        )
957
1
        .build()
958
1
        .execute_with(|| {
959
1
            UseSnowbridgeV2::set(&true);
960

            
961
1
            let token_location: VersionedLocation = Location::here()
962
1
            .into();
963

            
964
1
            assert_ok!(EthereumSystem::register_token(root_origin(), Box::new(token_location), snowbridge_core::AssetMetadata {
965
1
                name: "dance".as_bytes().to_vec().try_into().unwrap(),
966
1
                symbol: "dance".as_bytes().to_vec().try_into().unwrap(),
967
1
                decimals: 12,
968
1
		    }));
969

            
970

            
971
1
            run_to_block(2);
972

            
973
            // We can inject arbitraqry slashes for arbitary accounts with root
974
1
            let page_limit: u32 = <Runtime as pallet_external_validator_slashes::Config>::QueuedSlashesProcessedPerBlock::get();
975
11
            for i in 0..page_limit +1 {
976
11
                assert_ok!(ExternalValidatorSlashes::force_inject_slash(
977
11
                    RuntimeOrigin::root(),
978
                    0,
979
11
                    AccountId::new(H256::from_low_u64_be(u64::from(i)).to_fixed_bytes()),
980
11
                    Perbill::from_percent(75),
981
                    1
982
                ));
983
            }
984

            
985
1
            let deferred_era = ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
986

            
987
1
            let slashes = ExternalValidatorSlashes::slashes(deferred_era);
988
1
            assert_eq!(slashes.len() as u32, page_limit +1);
989

            
990
1
            let session_in_which_slashes_are_sent =
991
1
                (ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1)
992
1
                    * SessionsPerEra::get();
993
1
            run_to_session(session_in_which_slashes_are_sent);
994

            
995
1
            let outbound_msg_queue_event = System::events()
996
1
                .iter()
997
2
                .filter(|r| matches!(r.event,
998
                    RuntimeEvent::EthereumOutboundQueueV2(
999
                        snowbridge_pallet_outbound_queue_v2::Event::MessageQueued { .. },
                    )))
1
                .count();
            // We have two reasons for sending messages:
            // 1, because on_era_end sends rewards
            // 2, because on_era_start sends slashes
            // Both session ends and session starts are done on_initialize of frame-sesssion
1
            assert_eq!(
                outbound_msg_queue_event, 1,
                "MessageQueued event should be emitted"
            );
            // We still have all slashes as unprocessed
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
1
            assert_eq!(unprocessed_slashes.len() as u32, page_limit +1);
            // Slashes start being sent after the era block
            // They are scheduled as unprocessedSlashes
1
            run_block();
1
            let outbound_msg_queue_event = System::events()
1
                .iter()
2
                .filter(|r| matches!(r.event,
                    RuntimeEvent::EthereumOutboundQueueV2(
                        snowbridge_pallet_outbound_queue_v2::Event::MessageQueued { .. },
                    )))
1
                .count();
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
            // This one is related to slashes
1
            assert_eq!(
                outbound_msg_queue_event, 1,
                "MessageQueued event should be emitted"
            );
            // We still should have one pending unprocessed slash, to be sent in the next block
1
            assert_eq!(unprocessed_slashes.len() as u32, 1);
1
            run_block();
1
            let outbound_msg_queue_event = System::events()
1
                .iter()
2
                .filter(|r| matches!(r.event,
                    RuntimeEvent::EthereumOutboundQueueV2(
                        snowbridge_pallet_outbound_queue_v2::Event::MessageQueued { .. },
                    )
                ))
1
                .count();
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
            // This one is related to slashes
1
            assert_eq!(
                outbound_msg_queue_event, 1,
                "MessageQueued event should be emitted"
            );
            // Now we should have 0
1
            assert_eq!(unprocessed_slashes.len() as u32, 0);
            // EthereumOutboundQueue -> queue_message -> MessageQQueuePallet (queue)
            // MessageQueuePallet on_initialize -> dispatch queue -> process_message -> EthereumOutboundQueue_process_message
1
            let nonce = snowbridge_pallet_outbound_queue_v2::Nonce::<Runtime>::get();
            // We dispatched 3 already
            // 1 reward + 2 slashes
1
            assert_eq!(nonce, 3);
1
        });
1
}
#[test]
1
fn test_slashes_are_sent_to_ethereum_accumulate_until_next_era_v2() {
1
    sp_tracing::try_init_simple();
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_validators(
1
            vec![]
1
        )
1
        .with_external_validators(
1
            vec![
1
                (AccountId::from(ALICE), 210 * UNIT),
1
                (AccountId::from(BOB), 100 * UNIT),
1
            ]
1
        )
1
        .build()
1
        .execute_with(|| {
1
            UseSnowbridgeV2::set(&true);
1
            let token_location: VersionedLocation = Location::here()
1
            .into();
1
            assert_ok!(EthereumSystem::register_token(root_origin(), Box::new(token_location), snowbridge_core::AssetMetadata {
1
                name: "dance".as_bytes().to_vec().try_into().unwrap(),
1
                symbol: "dance".as_bytes().to_vec().try_into().unwrap(),
1
                decimals: 12,
1
		    }));
1
            run_to_block(2);
            // We can inject arbitraqry slashes for arbitary accounts with root
1
            let page_limit: u32 = <Runtime as pallet_external_validator_slashes::Config>::QueuedSlashesProcessedPerBlock::get();
1
            let blocks_in_era = crate::EpochDurationInBlocks::get() * SessionsPerEra::get();
1
            let total_slashes_to_inject = blocks_in_era*page_limit +1;
301
            for i in 0..total_slashes_to_inject {
301
                assert_ok!(ExternalValidatorSlashes::force_inject_slash(
301
                    RuntimeOrigin::root(),
                    0,
301
                    AccountId::new(H256::from_low_u64_be(u64::from(i)).to_fixed_bytes()),
301
                    Perbill::from_percent(75),
                    1
                ));
            }
1
            let deferred_era = ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1;
1
            let slashes = ExternalValidatorSlashes::slashes(deferred_era);
1
            assert_eq!(slashes.len() as u32, total_slashes_to_inject);
1
            let session_in_which_slashes_are_sent =
1
                (ExternalValidators::current_era().unwrap() + SlashDeferDuration::get() + 1)
1
                    * SessionsPerEra::get();
1
            run_to_session(session_in_which_slashes_are_sent);
1
            let outbound_msg_queue_event = System::events()
1
                .iter()
2
                .filter(|r| matches!(r.event,
                    RuntimeEvent::EthereumOutboundQueueV2(
                        snowbridge_pallet_outbound_queue_v2::Event::MessageQueued { .. },
                    )
                ))
1
                .count();
            // We have two reasons for sending messages:
            // 1, because on_era_end sends rewards
            // 2, because on_era_start sends slashes
            // Both session ends and session starts are done on_initialize of frame-sesssion
1
            assert_eq!(
                outbound_msg_queue_event, 1,
                "MessageQueued event should be emitted"
            );
            // We still have all slashes as unprocessed
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
1
            assert_eq!(unprocessed_slashes.len() as u32, total_slashes_to_inject);
            // Running to the next era, but we should still have unprocessed
1
            run_to_session((ExternalValidators::current_era().unwrap() +1)*SessionsPerEra::get());
1
            let unprocessed_slashes = ExternalValidatorSlashes::unreported_slashes();
            // We still should have one pending unprocessed slash, to be sent in the next block
1
            assert_eq!(unprocessed_slashes.len() as u32, 1);
            // And in this case, we have 2 events
            // the rewards one plus the one where we sent remaining slashes
1
            let outbound_msg_queue_event = System::events()
1
                .iter()
4
                .filter(|r| matches!(r.event,
                    RuntimeEvent::EthereumOutboundQueueV2(
                        snowbridge_pallet_outbound_queue_v2::Event::MessageQueued { .. },
                    )
                ))
1
                .count();
1
            assert_eq!(
                outbound_msg_queue_event, 2,
                "MessageQueued event should be emitted"
            );
1
        });
1
}
9
fn inject_babe_slash(seed: &str) {
9
    let babe_key = get_pair_from_seed::<babe_primitives::AuthorityId>(seed);
9
    let equivocation_proof = generate_babe_equivocation_proof(&babe_key);
    // create the key ownership proof
9
    let key = (babe_primitives::KEY_TYPE, babe_key.public());
9
    let key_owner_proof = Historical::prove(key).unwrap();
    // report the equivocation
9
    assert_ok!(Babe::report_equivocation_unsigned(
9
        RuntimeOrigin::none(),
9
        Box::new(equivocation_proof),
9
        key_owner_proof,
    ));
9
}
3
fn inject_grandpa_slash(seed: &str) {
3
    let grandpa_key = get_pair_from_seed::<grandpa_primitives::AuthorityId>(seed);
3
    let set_id = Grandpa::current_set_id();
3
    let equivocation_proof = generate_grandpa_equivocation_proof(
3
        set_id,
3
        (1, H256::random(), 1, &grandpa_key),
3
        (1, H256::random(), 1, &grandpa_key),
    );
    // create the key ownership proof
3
    let key = (grandpa_primitives::KEY_TYPE, grandpa_key.public());
3
    let key_owner_proof = Historical::prove(key).unwrap();
    // report the equivocation
3
    assert_ok!(Grandpa::report_equivocation_unsigned(
3
        RuntimeOrigin::none(),
3
        Box::new(equivocation_proof),
3
        key_owner_proof,
    ));
3
}