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
    pallet_registrar_runtime_api::ContainerChainGenesisData,
28
    parity_scale_codec::{Decode, Encode, MaxEncodedLen},
29
    sp_runtime::{traits::SaturatedConversion, BuildStorage, Digest, DigestItem},
30
    starlight_runtime::MaxLengthTokenSymbol,
31
};
32

            
33
pub use starlight_runtime::{
34
    genesis_config_presets::get_authority_keys_from_seed, AccountId, Babe, Balance, Balances,
35
    CollatorConfiguration, ContainerRegistrar, Initializer, Runtime, Session, System,
36
    TransactionPayment,
37
};
38

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

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

            
48
9
pub fn run_to_session(n: u32) {
49
9
    run_to_block(session_to_block(n));
50
9
}
51

            
52
/// Utility function that advances the chain to the desired block number.
53
///
54
/// After this function returns, the current block number will be `n`, and the block will be "open",
55
/// meaning that on_initialize has been executed, but on_finalize has not. To execute on_finalize as
56
/// well, for example to test a runtime api, manually call `end_block` after this, run the test, and
57
/// call `start_block` to ensure that this function keeps working as expected.
58
/// Extrinsics should always be executed before on_finalize.
59
12
pub fn run_to_block(n: u32) {
60
12
    let current_block_number = System::block_number();
61
12
    assert!(
62
12
        current_block_number < n,
63
        "run_to_block called with block {} when current block is {}",
64
        n,
65
        current_block_number
66
    );
67

            
68
112
    while System::block_number() < n {
69
100
        run_block();
70
100
    }
71
12
}
72

            
73
106
pub fn insert_authorities_and_slot_digests(slot: u64) {
74
106
    let pre_digest = Digest {
75
106
        logs: vec![DigestItem::PreRuntime(
76
106
            BABE_ENGINE_ID,
77
106
            PreDigest::SecondaryPlain(SecondaryPlainPreDigest {
78
106
                slot: slot.into(),
79
106
                authority_index: 0,
80
106
            })
81
106
            .encode(),
82
106
        )],
83
106
    };
84
106

            
85
106
    System::reset_events();
86
106
    System::initialize(
87
106
        &(System::block_number() + 1),
88
106
        &System::parent_hash(),
89
106
        &pre_digest,
90
106
    );
91
106
}
92

            
93
#[derive(Clone, Encode, Decode, PartialEq, Debug, scale_info::TypeInfo, MaxEncodedLen)]
94
enum RunBlockState {
95
100
    Start(u32),
96
100
    End(u32),
97
}
98

            
99
impl RunBlockState {
100
206
    fn assert_can_advance(&self, new_state: &RunBlockState) {
101
206
        match self {
102
100
            RunBlockState::Start(n) => {
103
100
                assert_eq!(
104
100
                    new_state,
105
100
                    &RunBlockState::End(*n),
106
                    "expected a call to end_block({}), but user called {:?}",
107
                    *n,
108
                    new_state
109
                );
110
            }
111
106
            RunBlockState::End(n) => {
112
106
                assert_eq!(
113
106
                    new_state,
114
106
                    &RunBlockState::Start(*n + 1),
115
                    "expected a call to start_block({}), but user called {:?}",
116
                    *n + 1,
117
                    new_state
118
                )
119
            }
120
        }
121
206
    }
122
}
123

            
124
206
fn advance_block_state_machine(new_state: RunBlockState) {
125
206
    if frame_support::storage::unhashed::exists(b"__mock_is_xcm_test") {
126
        // Disable this check in XCM tests, because the XCM emulator runs on_initialize and
127
        // on_finalize automatically
128
        return;
129
206
    }
130
206
    let old_state: RunBlockState =
131
206
        frame_support::storage::unhashed::get(b"__mock_debug_block_state").unwrap_or(
132
206
            // Initial state is expecting a call to start() with block number 1, so old state should be
133
206
            // end of block 0
134
206
            RunBlockState::End(0),
135
206
        );
136
206
    old_state.assert_can_advance(&new_state);
137
206
    frame_support::storage::unhashed::put(b"__mock_debug_block_state", &new_state);
138
206
}
139

            
140
106
pub fn start_block() {
141
106
    let block_number = System::block_number();
142
106
    advance_block_state_machine(RunBlockState::Start(block_number + 1));
143
106

            
144
106
    insert_authorities_and_slot_digests(current_slot() + 1);
145
106

            
146
106
    // Initialize the new block
147
106
    Babe::on_initialize(System::block_number());
148
106
    Session::on_initialize(System::block_number());
149
106
    Initializer::on_initialize(System::block_number());
150
106
}
151

            
152
100
pub fn end_block() {
153
100
    let block_number = System::block_number();
154
100
    advance_block_state_machine(RunBlockState::End(block_number));
155
100
    // Finalize the block
156
100
    Babe::on_finalize(System::block_number());
157
100
    Session::on_finalize(System::block_number());
158
100
    Initializer::on_finalize(System::block_number());
159
100
    TransactionPayment::on_finalize(System::block_number());
160
100
}
161

            
162
100
pub fn run_block() {
163
100
    end_block();
164
100

            
165
100
    start_block()
166
100
}
167

            
168
#[derive(Default, Clone)]
169
pub struct ParaRegistrationParams {
170
    para_id: u32,
171
    genesis_data: ContainerChainGenesisData<MaxLengthTokenSymbol>,
172
    block_production_credits: u32,
173
    collator_assignment_credits: u32,
174
}
175

            
176
impl
177
    From<(
178
        u32,
179
        ContainerChainGenesisData<MaxLengthTokenSymbol>,
180
        u32,
181
        u32,
182
    )> for ParaRegistrationParams
183
{
184
8
    fn from(
185
8
        value: (
186
8
            u32,
187
8
            ContainerChainGenesisData<MaxLengthTokenSymbol>,
188
8
            u32,
189
8
            u32,
190
8
        ),
191
8
    ) -> Self {
192
8
        Self {
193
8
            para_id: value.0,
194
8
            genesis_data: value.1,
195
8
            block_production_credits: value.2,
196
8
            collator_assignment_credits: value.3,
197
8
        }
198
8
    }
199
}
200

            
201
6
pub fn default_config() -> pallet_configuration::HostConfiguration {
202
6
    pallet_configuration::HostConfiguration {
203
6
        max_collators: 100,
204
6
        min_orchestrator_collators: 2,
205
6
        max_orchestrator_collators: 2,
206
6
        collators_per_container: 2,
207
6
        full_rotation_period: 0,
208
6
        ..Default::default()
209
6
    }
210
6
}
211

            
212
pub struct ExtBuilder {
213
    // endowed accounts with balances
214
    balances: Vec<(AccountId, Balance)>,
215
    // [validator, amount]
216
    validators: Vec<(AccountId, Balance)>,
217
    // [collator, amount]
218
    collators: Vec<(AccountId, Balance)>,
219
    // sudo key
220
    sudo: Option<AccountId>,
221
    // list of registered para ids: para_id, genesis_data, boot_nodes, block_credits, session_credits
222
    para_ids: Vec<ParaRegistrationParams>,
223
    // configuration to apply
224
    config: pallet_configuration::HostConfiguration,
225
    own_para_id: Option<ParaId>,
226
}
227

            
228
impl Default for ExtBuilder {
229
6
    fn default() -> Self {
230
6
        Self {
231
6
            balances: vec![
232
6
                // Alice gets 10k extra tokens for her mapping deposit
233
6
                (AccountId::from(ALICE), 210_000 * UNIT),
234
6
                (AccountId::from(BOB), 100_000 * UNIT),
235
6
            ],
236
6
            validators: vec![
237
6
                (AccountId::from(ALICE), 210 * UNIT),
238
6
                (AccountId::from(BOB), 100 * UNIT),
239
6
            ],
240
6
            collators: Default::default(),
241
6
            sudo: Default::default(),
242
6
            para_ids: Default::default(),
243
6
            config: default_config(),
244
6
            own_para_id: Default::default(),
245
6
        }
246
6
    }
247
}
248

            
249
impl ExtBuilder {
250
1
    pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
251
1
        self.balances = balances;
252
1
        self
253
1
    }
254

            
255
    pub fn with_sudo(mut self, sudo: AccountId) -> Self {
256
        self.sudo = Some(sudo);
257
        self
258
    }
259

            
260
    pub fn with_validators(mut self, validators: Vec<(AccountId, Balance)>) -> Self {
261
        self.validators = validators;
262
        self
263
    }
264

            
265
    pub fn with_collators(mut self, collators: Vec<(AccountId, Balance)>) -> Self {
266
        self.collators = collators;
267
        self
268
    }
269

            
270
4
    pub fn with_para_ids(mut self, para_ids: Vec<ParaRegistrationParams>) -> Self {
271
4
        self.para_ids = para_ids;
272
4
        self
273
4
    }
274

            
275
    // Maybe change to with_collators_config?
276
    pub fn with_config(mut self, config: pallet_configuration::HostConfiguration) -> Self {
277
        self.config = config;
278
        self
279
    }
280

            
281
6
    pub fn build_storage(self) -> sp_core::storage::Storage {
282
6
        let mut t = frame_system::GenesisConfig::<Runtime>::default()
283
6
            .build_storage()
284
6
            .unwrap();
285
6

            
286
6
        pallet_babe::GenesisConfig::<Runtime> {
287
6
            ..Default::default()
288
6
        }
289
6
        .assimilate_storage(&mut t)
290
6
        .unwrap();
291
6

            
292
6
        pallet_balances::GenesisConfig::<Runtime> {
293
6
            balances: self.balances,
294
6
        }
295
6
        .assimilate_storage(&mut t)
296
6
        .unwrap();
297
6

            
298
6
        // We need to initialize these pallets first. When initializing pallet-session,
299
6
        // these values will be taken into account for collator-assignment.
300
6

            
301
6
        pallet_registrar::GenesisConfig::<Runtime> {
302
6
            para_ids: self
303
6
                .para_ids
304
6
                .iter()
305
6
                .cloned()
306
8
                .map(|registered_para| {
307
8
                    (registered_para.para_id.into(), registered_para.genesis_data)
308
8
                })
309
6
                .collect(),
310
6
        }
311
6
        .assimilate_storage(&mut t)
312
6
        .unwrap();
313
6

            
314
6
        // TODO: add here pallet_services_payment::GenesisConfig
315
6

            
316
6
        pallet_configuration::GenesisConfig::<Runtime> {
317
6
            config: self.config,
318
6
            ..Default::default()
319
6
        }
320
6
        .assimilate_storage(&mut t)
321
6
        .unwrap();
322
6

            
323
6
        // TODO: add here pallet_xcm::GenesisConfig
324
6

            
325
6
        if !self.validators.is_empty() {
326
6
            let keys: Vec<_> = self
327
6
                .validators
328
6
                .into_iter()
329
12
                .map(|(account, _balance)| {
330
12
                    let authority_keys = get_authority_keys_from_seed(&account.to_string());
331
12
                    (
332
12
                        account.clone(),
333
12
                        account,
334
12
                        starlight_runtime::SessionKeys {
335
12
                            babe: authority_keys.2.clone(),
336
12
                            grandpa: authority_keys.3.clone(),
337
12
                            para_validator: authority_keys.4.clone(),
338
12
                            para_assignment: authority_keys.5.clone(),
339
12
                            authority_discovery: authority_keys.6.clone(),
340
12
                            beefy: authority_keys.7.clone(),
341
12
                        },
342
12
                    )
343
12
                })
344
6
                .collect();
345
6
            pallet_session::GenesisConfig::<Runtime> { keys }
346
6
                .assimilate_storage(&mut t)
347
6
                .unwrap();
348
6
        }
349

            
350
6
        pallet_sudo::GenesisConfig::<Runtime> { key: self.sudo }
351
6
            .assimilate_storage(&mut t)
352
6
            .unwrap();
353
6
        t
354
6
    }
355

            
356
6
    pub fn build(self) -> sp_io::TestExternalities {
357
6
        let t = self.build_storage();
358
6
        let mut ext = sp_io::TestExternalities::new(t);
359
6

            
360
6
        ext.execute_with(|| {
361
6
            // Start block 1
362
6
            start_block();
363
6
        });
364
6
        ext
365
6
    }
366
}
367

            
368
6
pub fn root_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
369
6
    <Runtime as frame_system::Config>::RuntimeOrigin::root()
370
6
}
371

            
372
1
pub fn origin_of(account_id: AccountId) -> <Runtime as frame_system::Config>::RuntimeOrigin {
373
1
    <Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id)
374
1
}
375

            
376
pub fn inherent_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
377
    <Runtime as frame_system::Config>::RuntimeOrigin::none()
378
}
379

            
380
7
pub fn empty_genesis_data() -> ContainerChainGenesisData<MaxLengthTokenSymbol> {
381
7
    ContainerChainGenesisData {
382
7
        storage: Default::default(),
383
7
        name: Default::default(),
384
7
        id: Default::default(),
385
7
        fork_id: Default::default(),
386
7
        extensions: Default::default(),
387
7
        properties: Default::default(),
388
7
    }
389
7
}
390

            
391
106
pub fn current_slot() -> u64 {
392
106
    Babe::current_slot().into()
393
106
}
394

            
395
pub const ALICE: [u8; 32] = [4u8; 32];
396
pub const BOB: [u8; 32] = [5u8; 32];
397
pub const CHARLIE: [u8; 32] = [6u8; 32];
398
pub const DAVE: [u8; 32] = [7u8; 32];
399
pub const EVE: [u8; 32] = [8u8; 32];
400
pub const FERDIE: [u8; 32] = [9u8; 32];