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
#![allow(dead_code)]
18

            
19
use {
20
    crate::UNIT,
21
    babe_primitives::{
22
        digests::{PreDigest, SecondaryPlainPreDigest},
23
        BABE_ENGINE_ID,
24
    },
25
    cumulus_primitives_core::ParaId,
26
    frame_support::traits::{OnFinalize, OnInitialize},
27
    nimbus_primitives::NimbusId,
28
    pallet_registrar_runtime_api::ContainerChainGenesisData,
29
    parity_scale_codec::{Decode, Encode, MaxEncodedLen},
30
    sp_runtime::{traits::SaturatedConversion, BuildStorage, Digest, DigestItem},
31
    starlight_runtime::MaxLengthTokenSymbol,
32
};
33

            
34
pub use starlight_runtime::{
35
    genesis_config_presets::get_authority_keys_from_seed, AccountId, Babe, Balance, Initializer,
36
    Runtime, Session, System, TanssiAuthorityAssignment, TanssiCollatorAssignment,
37
    TransactionPayment,
38
};
39

            
40
19
pub fn session_to_block(n: u32) -> u32 {
41
19
    // let block_number = flashbox_runtime::Period::get() * n;
42
19
    let block_number = Babe::current_epoch().duration.saturated_into::<u32>() * n;
43
19

            
44
19
    // Add 1 because the block that emits the NewSession event cannot contain any extrinsics,
45
19
    // so this is the first block of the new session that can actually be used
46
19
    block_number + 1
47
19
}
48

            
49
9
pub fn authorities() -> Vec<babe_primitives::AuthorityId> {
50
9
    let _session_index = Session::current_index();
51
9

            
52
9
    Babe::authorities()
53
9
        .iter()
54
18
        .map(|(key, _)| key.clone())
55
9
        .collect()
56
9
}
57

            
58
6
pub fn authorities_for_container(para_id: ParaId) -> Option<Vec<NimbusId>> {
59
6
    let session_index = Session::current_index();
60
6

            
61
6
    TanssiAuthorityAssignment::collator_container_chain(session_index)
62
6
        .expect("authorities should be set")
63
6
        .container_chains
64
6
        .get(&para_id)
65
6
        .cloned()
66
6
}
67

            
68
pub fn accounts_for_container(para_id: ParaId) -> Option<Vec<AccountId>> {
69
    TanssiCollatorAssignment::collator_container_chain()
70
        .container_chains
71
        .get(&para_id)
72
        .cloned()
73
}
74

            
75
19
pub fn run_to_session(n: u32) {
76
19
    run_to_block(session_to_block(n));
77
19
}
78

            
79
/// Utility function that advances the chain to the desired block number.
80
///
81
/// After this function returns, the current block number will be `n`, and the block will be "open",
82
/// meaning that on_initialize has been executed, but on_finalize has not. To execute on_finalize as
83
/// well, for example to test a runtime api, manually call `end_block` after this, run the test, and
84
/// call `start_block` to ensure that this function keeps working as expected.
85
/// Extrinsics should always be executed before on_finalize.
86
27
pub fn run_to_block(n: u32) {
87
27
    let current_block_number = System::block_number();
88
27
    assert!(
89
27
        current_block_number < n,
90
        "run_to_block called with block {} when current block is {}",
91
        n,
92
        current_block_number
93
    );
94

            
95
227
    while System::block_number() < n {
96
200
        run_block();
97
200
    }
98
27
}
99

            
100
211
pub fn insert_authorities_and_slot_digests(slot: u64) {
101
211
    let pre_digest = Digest {
102
211
        logs: vec![DigestItem::PreRuntime(
103
211
            BABE_ENGINE_ID,
104
211
            PreDigest::SecondaryPlain(SecondaryPlainPreDigest {
105
211
                slot: slot.into(),
106
211
                authority_index: 0,
107
211
            })
108
211
            .encode(),
109
211
        )],
110
211
    };
111
211

            
112
211
    System::reset_events();
113
211
    System::initialize(
114
211
        &(System::block_number() + 1),
115
211
        &System::parent_hash(),
116
211
        &pre_digest,
117
211
    );
118
211
}
119

            
120
#[derive(Clone, Encode, Decode, PartialEq, Debug, scale_info::TypeInfo, MaxEncodedLen)]
121
enum RunBlockState {
122
200
    Start(u32),
123
200
    End(u32),
124
}
125

            
126
impl RunBlockState {
127
411
    fn assert_can_advance(&self, new_state: &RunBlockState) {
128
411
        match self {
129
200
            RunBlockState::Start(n) => {
130
200
                assert_eq!(
131
200
                    new_state,
132
200
                    &RunBlockState::End(*n),
133
                    "expected a call to end_block({}), but user called {:?}",
134
                    *n,
135
                    new_state
136
                );
137
            }
138
211
            RunBlockState::End(n) => {
139
211
                assert_eq!(
140
211
                    new_state,
141
211
                    &RunBlockState::Start(*n + 1),
142
                    "expected a call to start_block({}), but user called {:?}",
143
                    *n + 1,
144
                    new_state
145
                )
146
            }
147
        }
148
411
    }
149
}
150

            
151
411
fn advance_block_state_machine(new_state: RunBlockState) {
152
411
    if frame_support::storage::unhashed::exists(b"__mock_is_xcm_test") {
153
        // Disable this check in XCM tests, because the XCM emulator runs on_initialize and
154
        // on_finalize automatically
155
        return;
156
411
    }
157
411
    let old_state: RunBlockState =
158
411
        frame_support::storage::unhashed::get(b"__mock_debug_block_state").unwrap_or(
159
411
            // Initial state is expecting a call to start() with block number 1, so old state should be
160
411
            // end of block 0
161
411
            RunBlockState::End(0),
162
411
        );
163
411
    old_state.assert_can_advance(&new_state);
164
411
    frame_support::storage::unhashed::put(b"__mock_debug_block_state", &new_state);
165
411
}
166

            
167
211
pub fn start_block() {
168
211
    let block_number = System::block_number();
169
211
    advance_block_state_machine(RunBlockState::Start(block_number + 1));
170
211

            
171
211
    insert_authorities_and_slot_digests(current_slot() + 1);
172
211

            
173
211
    // Initialize the new block
174
211
    Babe::on_initialize(System::block_number());
175
211
    Session::on_initialize(System::block_number());
176
211
    Initializer::on_initialize(System::block_number());
177
211
}
178

            
179
200
pub fn end_block() {
180
200
    let block_number = System::block_number();
181
200
    advance_block_state_machine(RunBlockState::End(block_number));
182
200
    // Finalize the block
183
200
    Babe::on_finalize(System::block_number());
184
200
    Session::on_finalize(System::block_number());
185
200
    Initializer::on_finalize(System::block_number());
186
200
    TransactionPayment::on_finalize(System::block_number());
187
200
}
188

            
189
200
pub fn run_block() {
190
200
    end_block();
191
200

            
192
200
    start_block()
193
200
}
194

            
195
#[derive(Default, Clone)]
196
pub struct ParaRegistrationParams {
197
    para_id: u32,
198
    genesis_data: ContainerChainGenesisData<MaxLengthTokenSymbol>,
199
    block_production_credits: u32,
200
    collator_assignment_credits: u32,
201
}
202

            
203
impl
204
    From<(
205
        u32,
206
        ContainerChainGenesisData<MaxLengthTokenSymbol>,
207
        u32,
208
        u32,
209
    )> for ParaRegistrationParams
210
{
211
11
    fn from(
212
11
        value: (
213
11
            u32,
214
11
            ContainerChainGenesisData<MaxLengthTokenSymbol>,
215
11
            u32,
216
11
            u32,
217
11
        ),
218
11
    ) -> Self {
219
11
        Self {
220
11
            para_id: value.0,
221
11
            genesis_data: value.1,
222
11
            block_production_credits: value.2,
223
11
            collator_assignment_credits: value.3,
224
11
        }
225
11
    }
226
}
227

            
228
11
pub fn default_config() -> pallet_configuration::HostConfiguration {
229
11
    pallet_configuration::HostConfiguration {
230
11
        max_collators: 100,
231
11
        min_orchestrator_collators: 2,
232
11
        max_orchestrator_collators: 2,
233
11
        collators_per_container: 2,
234
11
        full_rotation_period: 0,
235
11
        ..Default::default()
236
11
    }
237
11
}
238

            
239
pub struct ExtBuilder {
240
    // endowed accounts with balances
241
    balances: Vec<(AccountId, Balance)>,
242
    // [validator, amount]
243
    validators: Vec<(AccountId, Balance)>,
244
    // [collator, amount]
245
    collators: Vec<(AccountId, Balance)>,
246
    // sudo key
247
    sudo: Option<AccountId>,
248
    // list of registered para ids: para_id, genesis_data, boot_nodes, block_credits, session_credits
249
    para_ids: Vec<ParaRegistrationParams>,
250
    // configuration to apply
251
    config: pallet_configuration::HostConfiguration,
252
    own_para_id: Option<ParaId>,
253
}
254

            
255
impl Default for ExtBuilder {
256
11
    fn default() -> Self {
257
11
        Self {
258
11
            balances: vec![
259
11
                // Alice gets 10k extra tokens for her mapping deposit
260
11
                (AccountId::from(ALICE), 210_000 * UNIT),
261
11
                (AccountId::from(BOB), 100_000 * UNIT),
262
11
            ],
263
11
            validators: vec![
264
11
                (AccountId::from(ALICE), 210 * UNIT),
265
11
                (AccountId::from(BOB), 100 * UNIT),
266
11
            ],
267
11
            collators: Default::default(),
268
11
            sudo: Default::default(),
269
11
            para_ids: Default::default(),
270
11
            config: default_config(),
271
11
            own_para_id: Default::default(),
272
11
        }
273
11
    }
274
}
275

            
276
impl ExtBuilder {
277
6
    pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
278
6
        self.balances = balances;
279
6
        self
280
6
    }
281

            
282
    pub fn with_sudo(mut self, sudo: AccountId) -> Self {
283
        self.sudo = Some(sudo);
284
        self
285
    }
286

            
287
    pub fn with_validators(mut self, validators: Vec<(AccountId, Balance)>) -> Self {
288
        self.validators = validators;
289
        self
290
    }
291

            
292
5
    pub fn with_collators(mut self, collators: Vec<(AccountId, Balance)>) -> Self {
293
5
        self.collators = collators;
294
5
        self
295
5
    }
296

            
297
7
    pub fn with_para_ids(mut self, para_ids: Vec<ParaRegistrationParams>) -> Self {
298
7
        self.para_ids = para_ids;
299
7
        self
300
7
    }
301

            
302
    // Maybe change to with_collators_config?
303
5
    pub fn with_config(mut self, config: pallet_configuration::HostConfiguration) -> Self {
304
5
        self.config = config;
305
5
        self
306
5
    }
307

            
308
11
    pub fn build_storage(self) -> sp_core::storage::Storage {
309
11
        let mut t = frame_system::GenesisConfig::<Runtime>::default()
310
11
            .build_storage()
311
11
            .unwrap();
312
11

            
313
11
        pallet_babe::GenesisConfig::<Runtime> {
314
11
            ..Default::default()
315
11
        }
316
11
        .assimilate_storage(&mut t)
317
11
        .unwrap();
318
11

            
319
11
        pallet_balances::GenesisConfig::<Runtime> {
320
11
            balances: self.balances,
321
11
        }
322
11
        .assimilate_storage(&mut t)
323
11
        .unwrap();
324
11

            
325
11
        // We need to initialize these pallets first. When initializing pallet-session,
326
11
        // these values will be taken into account for collator-assignment.
327
11

            
328
11
        pallet_registrar::GenesisConfig::<Runtime> {
329
11
            para_ids: self
330
11
                .para_ids
331
11
                .iter()
332
11
                .cloned()
333
13
                .map(|registered_para| {
334
11
                    (registered_para.para_id.into(), registered_para.genesis_data)
335
13
                })
336
11
                .collect(),
337
11
        }
338
11
        .assimilate_storage(&mut t)
339
11
        .unwrap();
340
11

            
341
11
        // TODO: add here pallet_services_payment::GenesisConfig
342
11

            
343
11
        pallet_configuration::GenesisConfig::<Runtime> {
344
11
            config: self.config,
345
11
            ..Default::default()
346
11
        }
347
11
        .assimilate_storage(&mut t)
348
11
        .unwrap();
349
11

            
350
11
        let mut keys: Vec<_> = Vec::new();
351
11
        if !self.validators.is_empty() {
352
11
            let validator_keys: Vec<_> = self
353
11
                .validators
354
11
                .clone()
355
11
                .into_iter()
356
22
                .map(|(account, _balance)| {
357
22
                    let authority_keys = get_authority_keys_from_seed(&account.to_string());
358
22
                    (
359
22
                        account.clone(),
360
22
                        account,
361
22
                        starlight_runtime::SessionKeys {
362
22
                            babe: authority_keys.babe.clone(),
363
22
                            grandpa: authority_keys.grandpa.clone(),
364
22
                            para_validator: authority_keys.para_validator.clone(),
365
22
                            para_assignment: authority_keys.para_assignment.clone(),
366
22
                            authority_discovery: authority_keys.authority_discovery.clone(),
367
22
                            beefy: authority_keys.beefy.clone(),
368
22
                            nimbus: authority_keys.nimbus.clone(),
369
22
                        },
370
22
                    )
371
22
                })
372
11
                .collect();
373
11
            keys.extend(validator_keys)
374
        }
375

            
376
11
        if !self.collators.is_empty() {
377
            // We set invulnerables in pallet_invulnerables
378
5
            let invulnerables: Vec<AccountId> = self
379
5
                .collators
380
5
                .clone()
381
5
                .into_iter()
382
10
                .map(|(account, _balance)| account)
383
5
                .collect();
384
5

            
385
5
            pallet_invulnerables::GenesisConfig::<Runtime> {
386
5
                invulnerables: invulnerables.clone(),
387
5
            }
388
5
            .assimilate_storage(&mut t)
389
5
            .unwrap();
390
5

            
391
5
            // But we also initialize their keys in the session pallet
392
5
            // We discard those that had the key initialized already
393
5
            // from the validator list
394
5
            // in other words, for testing purposes we allow to inject a validator account
395
5
            // in the collator list
396
5
            let validator_unique_accounts: Vec<_> = self
397
5
                .validators
398
5
                .iter()
399
10
                .map(|(account, _)| account.clone())
400
5
                .collect();
401
5
            let collator_keys: Vec<_> = self
402
5
                .collators
403
5
                .into_iter()
404
10
                .filter_map(|(account, _balance)| {
405
10
                    if validator_unique_accounts.contains(&account) {
406
10
                        None
407
                    } else {
408
                        let authority_keys = get_authority_keys_from_seed(&account.to_string());
409
                        Some((
410
                            account.clone(),
411
                            account,
412
                            starlight_runtime::SessionKeys {
413
                                babe: authority_keys.babe.clone(),
414
                                grandpa: authority_keys.grandpa.clone(),
415
                                para_validator: authority_keys.para_validator.clone(),
416
                                para_assignment: authority_keys.para_assignment.clone(),
417
                                authority_discovery: authority_keys.authority_discovery.clone(),
418
                                beefy: authority_keys.beefy.clone(),
419
                                nimbus: authority_keys.nimbus.clone(),
420
                            },
421
                        ))
422
                    }
423
10
                })
424
5
                .collect();
425
5
            keys.extend(collator_keys)
426
6
        }
427

            
428
11
        pallet_session::GenesisConfig::<Runtime> { keys }
429
11
            .assimilate_storage(&mut t)
430
11
            .unwrap();
431
11

            
432
11
        pallet_sudo::GenesisConfig::<Runtime> { key: self.sudo }
433
11
            .assimilate_storage(&mut t)
434
11
            .unwrap();
435
11
        t
436
11
    }
437

            
438
11
    pub fn build(self) -> sp_io::TestExternalities {
439
11
        let t = self.build_storage();
440
11
        let mut ext = sp_io::TestExternalities::new(t);
441
11

            
442
11
        ext.execute_with(|| {
443
11
            // Start block 1
444
11
            start_block();
445
11
        });
446
11
        ext
447
11
    }
448
}
449

            
450
14
pub fn root_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
451
14
    <Runtime as frame_system::Config>::RuntimeOrigin::root()
452
14
}
453

            
454
10
pub fn origin_of(account_id: AccountId) -> <Runtime as frame_system::Config>::RuntimeOrigin {
455
10
    <Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id)
456
10
}
457

            
458
pub fn inherent_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
459
    <Runtime as frame_system::Config>::RuntimeOrigin::none()
460
}
461

            
462
12
pub fn empty_genesis_data() -> ContainerChainGenesisData<MaxLengthTokenSymbol> {
463
12
    ContainerChainGenesisData {
464
12
        storage: Default::default(),
465
12
        name: Default::default(),
466
12
        id: Default::default(),
467
12
        fork_id: Default::default(),
468
12
        extensions: Default::default(),
469
12
        properties: Default::default(),
470
12
    }
471
12
}
472

            
473
211
pub fn current_slot() -> u64 {
474
211
    Babe::current_slot().into()
475
211
}
476

            
477
pub const ALICE: [u8; 32] = [4u8; 32];
478
pub const BOB: [u8; 32] = [5u8; 32];
479
pub const CHARLIE: [u8; 32] = [6u8; 32];
480
pub const DAVE: [u8; 32] = [7u8; 32];
481
pub const EVE: [u8; 32] = [8u8; 32];
482
pub const FERDIE: [u8; 32] = [9u8; 32];