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::{AuthorInherent, BlockProductionCost, CollatorAssignmentCost},
19
    cumulus_primitives_core::{ParaId, PersistedValidationData},
20
    cumulus_primitives_parachain_inherent::ParachainInherentData,
21
    dp_consensus::runtime_decl_for_tanssi_authority_assignment_api::TanssiAuthorityAssignmentApi,
22
    frame_support::{
23
        assert_ok,
24
        traits::{OnFinalize, OnInitialize},
25
    },
26
    nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID},
27
    pallet_collator_assignment_runtime_api::runtime_decl_for_collator_assignment_api::CollatorAssignmentApi,
28
    pallet_registrar_runtime_api::ContainerChainGenesisData,
29
    pallet_services_payment::{ProvideBlockProductionCost, ProvideCollatorAssignmentCost},
30
    parity_scale_codec::{Decode, Encode, MaxEncodedLen},
31
    polkadot_parachain_primitives::primitives::HeadData,
32
    sp_consensus_aura::AURA_ENGINE_ID,
33
    sp_consensus_slots::Slot,
34
    sp_core::{Get, Pair},
35
    sp_runtime::{traits::Dispatchable, BuildStorage, Digest, DigestItem},
36
    sp_std::collections::btree_map::BTreeMap,
37
    test_relay_sproof_builder::ParaHeaderSproofBuilder,
38
};
39

            
40
pub use crate::{
41
    AccountId, AuthorNoting, AuthorityAssignment, AuthorityMapping, Balance, Balances,
42
    CollatorAssignment, Configuration, DataPreservers, InflationRewards, Initializer,
43
    Invulnerables, ParachainInfo, Proxy, ProxyType, Registrar, RewardsPortion, Runtime,
44
    RuntimeCall, ServicesPayment, Session, StreamPayment, System, TransactionPayment,
45
};
46

            
47
pub const UNIT: Balance = 1_000_000_000_000;
48

            
49
77
pub fn session_to_block(n: u32) -> u32 {
50
77
    let block_number = crate::Period::get() * n;
51
77

            
52
77
    // Add 1 because the block that emits the NewSession event cannot contain any extrinsics,
53
77
    // so this is the first block of the new session that can actually be used
54
77
    block_number + 1
55
77
}
56

            
57
#[derive(Debug, Clone, Eq, PartialEq)]
58
pub struct RunSummary {
59
    pub author_id: AccountId,
60
    pub inflation: Balance,
61
}
62

            
63
77
pub fn run_to_session(n: u32) {
64
77
    run_to_block(session_to_block(n));
65
77
}
66

            
67
/// Utility function that advances the chain to the desired block number.
68
///
69
/// After this function returns, the current block number will be `n`, and the block will be "open",
70
/// meaning that on_initialize has been executed, but on_finalize has not. To execute on_finalize as
71
/// well, for example to test a runtime api, manually call `end_block` after this, run the test, and
72
/// call `start_block` to ensure that this function keeps working as expected.
73
/// Extrinsics should always be executed before on_finalize.
74
123
pub fn run_to_block(n: u32) -> BTreeMap<u32, RunSummary> {
75
123
    let current_block_number = System::block_number();
76
123
    assert!(
77
123
        current_block_number < n,
78
        "run_to_block called with block {} when current block is {}",
79
        n,
80
        current_block_number
81
    );
82
123
    let mut summaries = BTreeMap::new();
83

            
84
1096
    while System::block_number() < n {
85
973
        let summary = run_block();
86
973
        let block_number = System::block_number();
87
973
        summaries.insert(block_number, summary);
88
973
    }
89

            
90
123
    summaries
91
123
}
92

            
93
1057
pub fn insert_authorities_and_slot_digests(slot: u64) {
94
1057
    let authorities =
95
1057
        Runtime::para_id_authorities(ParachainInfo::get()).expect("authorities should be set");
96
1057

            
97
1057
    let authority: NimbusId = authorities[slot as usize % authorities.len()].clone();
98
1057

            
99
1057
    let pre_digest = Digest {
100
1057
        logs: vec![
101
1057
            DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode()),
102
1057
            DigestItem::PreRuntime(NIMBUS_ENGINE_ID, authority.encode()),
103
1057
        ],
104
1057
    };
105
1057

            
106
1057
    System::reset_events();
107
1057
    System::initialize(
108
1057
        &(System::block_number() + 1),
109
1057
        &System::parent_hash(),
110
1057
        &pre_digest,
111
1057
    );
112
1057
}
113

            
114
// Used to create the next block inherent data
115
#[derive(Clone, Encode, Decode, Default, PartialEq, Debug, scale_info::TypeInfo, MaxEncodedLen)]
116
pub struct MockInherentData {
117
    pub random_seed: Option<[u8; 32]>,
118
}
119

            
120
1057
fn take_new_inherent_data() -> Option<MockInherentData> {
121
1057
    let data: Option<MockInherentData> =
122
1057
        frame_support::storage::unhashed::take(b"__mock_new_inherent_data");
123
1057

            
124
1057
    data
125
1057
}
126

            
127
#[derive(Clone, Encode, Decode, PartialEq, Debug, scale_info::TypeInfo, MaxEncodedLen)]
128
enum RunBlockState {
129
987
    Start(u32),
130
986
    End(u32),
131
}
132

            
133
impl RunBlockState {
134
2044
    fn assert_can_advance(&self, new_state: &RunBlockState) {
135
2044
        match self {
136
987
            RunBlockState::Start(n) => {
137
987
                assert_eq!(
138
987
                    new_state,
139
987
                    &RunBlockState::End(*n),
140
                    "expected a call to end_block({}), but user called {:?}",
141
                    *n,
142
                    new_state
143
                );
144
            }
145
1057
            RunBlockState::End(n) => {
146
1057
                assert_eq!(
147
1057
                    new_state,
148
1057
                    &RunBlockState::Start(*n + 1),
149
                    "expected a call to start_block({}), but user called {:?}",
150
                    *n + 1,
151
                    new_state
152
                )
153
            }
154
        }
155
2044
    }
156
}
157

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

            
174
1057
pub fn start_block() -> RunSummary {
175
1057
    let block_number = System::block_number();
176
1057
    advance_block_state_machine(RunBlockState::Start(block_number + 1));
177
1057
    let mut slot = current_slot() + 1;
178
1057
    if block_number == 0 {
179
71
        // Hack to avoid breaking all tests. When the current block is 1, the slot number should be
180
71
        // 1. But all of our tests assume it will be 0. So use slot number = block_number - 1.
181
71
        slot = 0;
182
986
    }
183

            
184
1057
    let maybe_mock_inherent = take_new_inherent_data();
185

            
186
1057
    if let Some(mock_inherent_data) = maybe_mock_inherent {
187
        set_parachain_inherent_data(mock_inherent_data);
188
1057
    }
189

            
190
1057
    insert_authorities_and_slot_digests(slot);
191
1057

            
192
1057
    // Initialize the new block
193
1057
    CollatorAssignment::on_initialize(System::block_number());
194
1057
    Session::on_initialize(System::block_number());
195
1057
    Initializer::on_initialize(System::block_number());
196
1057
    AuthorInherent::on_initialize(System::block_number());
197
1057

            
198
1057
    // `Initializer::on_finalize` needs to run at least one to have
199
1057
    // author mapping setup.
200
1057
    let author_id = current_author();
201
1057

            
202
1057
    let current_issuance = Balances::total_issuance();
203
1057
    InflationRewards::on_initialize(System::block_number());
204
1057
    let new_issuance = Balances::total_issuance();
205
1057

            
206
1057
    frame_support::storage::unhashed::put(
207
1057
        &frame_support::storage::storage_prefix(b"AsyncBacking", b"SlotInfo"),
208
1057
        // TODO: this should be 0?
209
1057
        &(Slot::from(slot), 1),
210
1057
    );
211
1057

            
212
1057
    pallet_author_inherent::Pallet::<Runtime>::kick_off_authorship_validation(None.into())
213
1057
        .expect("author inherent to dispatch correctly");
214
1057

            
215
1057
    RunSummary {
216
1057
        author_id,
217
1057
        inflation: new_issuance - current_issuance,
218
1057
    }
219
1057
}
220

            
221
987
pub fn end_block() {
222
987
    let block_number = System::block_number();
223
987
    advance_block_state_machine(RunBlockState::End(block_number));
224
987
    // Finalize the block
225
987
    CollatorAssignment::on_finalize(System::block_number());
226
987
    Session::on_finalize(System::block_number());
227
987
    Initializer::on_finalize(System::block_number());
228
987
    AuthorInherent::on_finalize(System::block_number());
229
987
    TransactionPayment::on_finalize(System::block_number());
230
987
}
231

            
232
984
pub fn run_block() -> RunSummary {
233
984
    end_block();
234
984

            
235
984
    start_block()
236
984
}
237

            
238
/// Mock the inherent that sets validation data in ParachainSystem, which
239
/// contains the `relay_chain_block_number`, which is used in `collator-assignment` as a
240
/// source of randomness.
241
71
pub fn set_parachain_inherent_data(mock_inherent_data: MockInherentData) {
242
    use {
243
        cumulus_primitives_core::relay_chain::well_known_keys,
244
        cumulus_test_relay_sproof_builder::RelayStateSproofBuilder,
245
    };
246

            
247
71
    let relay_sproof = RelayStateSproofBuilder {
248
71
        para_id: 100u32.into(),
249
71
        included_para_head: Some(HeadData(vec![1, 2, 3])),
250
71
        current_slot: (current_slot()).into(),
251
71
        additional_key_values: if mock_inherent_data.random_seed.is_some() {
252
            vec![(
253
                well_known_keys::CURRENT_BLOCK_RANDOMNESS.to_vec(),
254
                Some(mock_inherent_data.random_seed).encode(),
255
            )]
256
        } else {
257
71
            vec![]
258
        },
259
71
        ..Default::default()
260
71
    };
261
71

            
262
71
    let (relay_parent_storage_root, relay_chain_state) = relay_sproof.into_state_root_and_proof();
263
71
    let vfp = PersistedValidationData {
264
71
        relay_parent_number: 1u32,
265
71
        relay_parent_storage_root,
266
71
        ..Default::default()
267
71
    };
268
71
    let parachain_inherent_data = ParachainInherentData {
269
71
        validation_data: vfp,
270
71
        relay_chain_state,
271
71
        downward_messages: Default::default(),
272
71
        horizontal_messages: Default::default(),
273
71
    };
274
71

            
275
71
    // Delete existing flag to avoid error
276
71
    // 'ValidationData must be updated only once in a block'
277
71
    // TODO: this is a hack
278
71
    frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix(
279
71
        b"ParachainSystem",
280
71
        b"ValidationData",
281
71
    ));
282
71

            
283
71
    assert_ok!(RuntimeCall::ParachainSystem(
284
71
        cumulus_pallet_parachain_system::Call::<Runtime>::set_validation_data {
285
71
            data: parachain_inherent_data
286
71
        }
287
71
    )
288
71
    .dispatch(inherent_origin()));
289
71
}
290

            
291
#[derive(Default, Clone)]
292
pub struct ParaRegistrationParams {
293
    pub para_id: u32,
294
    pub genesis_data: ContainerChainGenesisData,
295
    pub block_production_credits: u32,
296
    pub collator_assignment_credits: u32,
297
    pub parathread_params: Option<tp_traits::ParathreadParams>,
298
}
299

            
300
71
pub fn default_config() -> pallet_configuration::HostConfiguration {
301
71
    pallet_configuration::HostConfiguration {
302
71
        max_collators: 100,
303
71
        min_orchestrator_collators: 2,
304
71
        max_orchestrator_collators: 2,
305
71
        collators_per_container: 2,
306
71
        full_rotation_period: 0,
307
71
        ..Default::default()
308
71
    }
309
71
}
310

            
311
pub struct ExtBuilder {
312
    // endowed accounts with balances
313
    balances: Vec<(AccountId, Balance)>,
314
    // [collator, amount]
315
    collators: Vec<(AccountId, Balance)>,
316
    // sudo key
317
    sudo: Option<AccountId>,
318
    // list of registered para ids: para_id, genesis_data, boot_nodes, block_credits, session_credits
319
    para_ids: Vec<ParaRegistrationParams>,
320
    // configuration to apply
321
    config: pallet_configuration::HostConfiguration,
322
    own_para_id: Option<ParaId>,
323
}
324

            
325
impl Default for ExtBuilder {
326
71
    fn default() -> Self {
327
71
        Self {
328
71
            balances: vec![
329
71
                // Alice gets 10k extra tokens for her mapping deposit
330
71
                (AccountId::from(ALICE), 210_000 * UNIT),
331
71
                (AccountId::from(BOB), 100_000 * UNIT),
332
71
            ],
333
71
            collators: vec![
334
71
                (AccountId::from(ALICE), 210 * UNIT),
335
71
                (AccountId::from(BOB), 100 * UNIT),
336
71
            ],
337
71
            sudo: Default::default(),
338
71
            para_ids: Default::default(),
339
71
            config: default_config(),
340
71
            own_para_id: Default::default(),
341
71
        }
342
71
    }
343
}
344

            
345
impl ExtBuilder {
346
60
    pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
347
60
        self.balances = balances;
348
60
        self
349
60
    }
350

            
351
7
    pub fn with_sudo(mut self, sudo: AccountId) -> Self {
352
7
        self.sudo = Some(sudo);
353
7
        self
354
7
    }
355

            
356
39
    pub fn with_collators(mut self, collators: Vec<(AccountId, Balance)>) -> Self {
357
39
        self.collators = collators;
358
39
        self
359
39
    }
360

            
361
2
    pub fn with_para_ids(mut self, para_ids: Vec<ParaRegistrationParams>) -> Self {
362
2
        self.para_ids = para_ids;
363
2
        self
364
2
    }
365

            
366
    /// Helper function like `with_para_ids` but registering parachains with an empty genesis data,
367
    /// and max amount of credits.
368
28
    pub fn with_empty_parachains(mut self, para_ids: Vec<u32>) -> Self {
369
28
        self.para_ids = para_ids
370
28
            .into_iter()
371
59
            .map(|para_id| ParaRegistrationParams {
372
59
                para_id,
373
59
                genesis_data: empty_genesis_data(),
374
59
                block_production_credits: u32::MAX,
375
59
                collator_assignment_credits: u32::MAX,
376
59
                parathread_params: None,
377
59
            })
378
28
            .collect();
379
28
        self
380
28
    }
381

            
382
2
    pub fn with_config(mut self, config: pallet_configuration::HostConfiguration) -> Self {
383
2
        self.config = config;
384
2
        self
385
2
    }
386

            
387
71
    pub fn build_storage(self) -> sp_core::storage::Storage {
388
71
        let mut t = frame_system::GenesisConfig::<Runtime>::default()
389
71
            .build_storage()
390
71
            .unwrap();
391
71

            
392
71
        pallet_balances::GenesisConfig::<Runtime> {
393
71
            balances: self.balances,
394
71
        }
395
71
        .assimilate_storage(&mut t)
396
71
        .unwrap();
397
71

            
398
71
        // We need to initialize these pallets first. When initializing pallet-session,
399
71
        // these values will be taken into account for collator-assignment.
400
71

            
401
71
        pallet_registrar::GenesisConfig::<Runtime> {
402
71
            para_ids: self
403
71
                .para_ids
404
71
                .iter()
405
71
                .cloned()
406
71
                .map(|registered_para| {
407
62
                    (
408
62
                        registered_para.para_id.into(),
409
62
                        registered_para.genesis_data,
410
62
                        registered_para.parathread_params,
411
62
                    )
412
71
                })
413
71
                .collect(),
414
71
            phantom: Default::default(),
415
71
        }
416
71
        .assimilate_storage(&mut t)
417
71
        .unwrap();
418
71

            
419
71
        pallet_services_payment::GenesisConfig::<Runtime> {
420
71
            para_id_credits: self
421
71
                .para_ids
422
71
                .clone()
423
71
                .into_iter()
424
71
                .map(|registered_para| {
425
62
                    (
426
62
                        registered_para.para_id.into(),
427
62
                        registered_para.block_production_credits,
428
62
                        registered_para.collator_assignment_credits,
429
62
                    )
430
62
                        .into()
431
71
                })
432
71
                .collect(),
433
71
        }
434
71
        .assimilate_storage(&mut t)
435
71
        .unwrap();
436
71

            
437
71
        pallet_configuration::GenesisConfig::<Runtime> {
438
71
            config: self.config,
439
71
            ..Default::default()
440
71
        }
441
71
        .assimilate_storage(&mut t)
442
71
        .unwrap();
443

            
444
71
        if let Some(own_para_id) = self.own_para_id {
445
            parachain_info::GenesisConfig::<Runtime> {
446
                parachain_id: own_para_id,
447
                ..Default::default()
448
            }
449
            .assimilate_storage(&mut t)
450
            .unwrap();
451
71
        }
452

            
453
71
        if !self.collators.is_empty() {
454
71
            // We set invulnerables in pallet_invulnerables
455
71
            let invulnerables: Vec<AccountId> = self
456
71
                .collators
457
71
                .clone()
458
71
                .into_iter()
459
213
                .map(|(account, _balance)| account)
460
71
                .collect();
461
71

            
462
71
            pallet_invulnerables::GenesisConfig::<Runtime> {
463
71
                invulnerables: invulnerables.clone(),
464
71
            }
465
71
            .assimilate_storage(&mut t)
466
71
            .unwrap();
467
71

            
468
71
            // But we also initialize their keys in the session pallet
469
71
            let keys: Vec<_> = self
470
71
                .collators
471
71
                .into_iter()
472
213
                .map(|(account, _balance)| {
473
213
                    let nimbus_id = get_aura_id_from_seed(&account.to_string());
474
213
                    (
475
213
                        account.clone(),
476
213
                        account,
477
213
                        crate::SessionKeys { nimbus: nimbus_id },
478
213
                    )
479
213
                })
480
71
                .collect();
481
71
            pallet_session::GenesisConfig::<Runtime> {
482
71
                keys,
483
71
                ..Default::default()
484
71
            }
485
71
            .assimilate_storage(&mut t)
486
71
            .unwrap();
487
71
        }
488
71
        pallet_sudo::GenesisConfig::<Runtime> { key: self.sudo }
489
71
            .assimilate_storage(&mut t)
490
71
            .unwrap();
491
71
        t
492
71
    }
493

            
494
71
    pub fn build(self) -> sp_io::TestExternalities {
495
71
        let t = self.build_storage();
496
71
        let mut ext = sp_io::TestExternalities::new(t);
497
71

            
498
71
        ext.execute_with(|| {
499
71
            // Start block 1
500
71
            start_block();
501
71
            set_parachain_inherent_data(Default::default());
502
71
        });
503
71
        ext
504
71
    }
505
}
506

            
507
78
pub fn root_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
508
78
    <Runtime as frame_system::Config>::RuntimeOrigin::root()
509
78
}
510

            
511
118
pub fn origin_of(account_id: AccountId) -> <Runtime as frame_system::Config>::RuntimeOrigin {
512
118
    <Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id)
513
118
}
514

            
515
79
pub fn inherent_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
516
79
    <Runtime as frame_system::Config>::RuntimeOrigin::none()
517
79
}
518

            
519
/// Helper function to generate a crypto pair from seed
520
285
pub fn get_aura_id_from_seed(seed: &str) -> NimbusId {
521
285
    sp_core::sr25519::Pair::from_string(&format!("//{}", seed), None)
522
285
        .expect("static values are valid; qed")
523
285
        .public()
524
285
        .into()
525
285
}
526

            
527
4
pub fn get_orchestrator_current_author() -> Option<AccountId> {
528
4
    let slot: u64 = current_slot();
529
4
    let orchestrator_collators = Runtime::parachain_collators(ParachainInfo::get())?;
530
4
    let author_index = slot % orchestrator_collators.len() as u64;
531
4
    let account = orchestrator_collators.get(author_index as usize)?;
532
4
    Some(account.clone())
533
4
}
534
/// Mocks the author noting inherent to insert the data we
535
8
pub fn set_author_noting_inherent_data(builder: ParaHeaderSproofBuilder) {
536
8
    let (relay_storage_root, relay_storage_proof) = builder.into_state_root_and_proof();
537
8

            
538
8
    // For now we directly touch parachain_system storage to set the relay state root.
539
8
    // TODO: Properly set the parachain_system inherent, which require a sproof builder combining
540
8
    // what is required by parachain_system and author_noting.
541
8
    frame_support::storage::unhashed::put(
542
8
        &frame_support::storage::storage_prefix(b"ParachainSystem", b"ValidationData"),
543
8
        &PersistedValidationData {
544
8
            parent_head: HeadData(Default::default()),
545
8
            relay_parent_number: 0u32,
546
8
            relay_parent_storage_root: relay_storage_root,
547
8
            max_pov_size: 0u32,
548
8
        },
549
8
    );
550
8

            
551
8
    // But we also need to store the new proof submitted
552
8
    frame_support::storage::unhashed::put(
553
8
        &frame_support::storage::storage_prefix(b"ParachainSystem", b"RelayStateProof"),
554
8
        &relay_storage_proof,
555
8
    );
556
8

            
557
8
    assert_ok!(RuntimeCall::AuthorNoting(
558
8
        pallet_author_noting::Call::<Runtime>::set_latest_author_data {
559
8
            data: tp_author_noting_inherent::OwnParachainInherentData {
560
8
                relay_storage_proof,
561
8
            }
562
8
        }
563
8
    )
564
8
    .dispatch(inherent_origin()));
565
8
}
566

            
567
88
pub fn empty_genesis_data() -> ContainerChainGenesisData {
568
88
    ContainerChainGenesisData {
569
88
        storage: Default::default(),
570
88
        name: Default::default(),
571
88
        id: Default::default(),
572
88
        fork_id: Default::default(),
573
88
        extensions: Default::default(),
574
88
        properties: Default::default(),
575
88
    }
576
88
}
577

            
578
1155
pub fn current_slot() -> u64 {
579
1155
    u64::from(
580
1155
        pallet_async_backing::SlotInfo::<Runtime>::get()
581
1155
            .unwrap_or_default()
582
1155
            .0,
583
1155
    )
584
1155
}
585

            
586
31
pub fn authorities() -> Vec<NimbusId> {
587
31
    let session_index = Session::current_index();
588
31

            
589
31
    AuthorityAssignment::collator_container_chain(session_index)
590
31
        .expect("authorities should be set")
591
31
        .orchestrator_chain
592
31
}
593

            
594
1084
pub fn current_author() -> AccountId {
595
1084
    let current_session = Session::current_index();
596
1084
    let mapping =
597
1084
        pallet_authority_mapping::Pallet::<Runtime>::authority_id_mapping(current_session)
598
1084
            .expect("there is a mapping for the current session");
599
1084

            
600
1084
    let author = pallet_author_inherent::Author::<Runtime>::get()
601
1084
        .expect("there should be a registered author");
602
1084

            
603
1084
    mapping
604
1084
        .get(&author)
605
1084
        .expect("there is a mapping for the current author")
606
1084
        .clone()
607
1084
}
608

            
609
15
pub fn block_credits_to_required_balance(number_of_blocks: u32, para_id: ParaId) -> Balance {
610
15
    let block_cost = BlockProductionCost::block_cost(&para_id).0;
611
15
    u128::from(number_of_blocks).saturating_mul(block_cost)
612
15
}
613

            
614
4
pub fn collator_assignment_credits_to_required_balance(
615
4
    number_of_sessions: u32,
616
4
    para_id: ParaId,
617
4
) -> Balance {
618
4
    let collator_assignment_cost = CollatorAssignmentCost::collator_assignment_cost(&para_id).0;
619
4
    u128::from(number_of_sessions).saturating_mul(collator_assignment_cost)
620
4
}
621

            
622
pub const ALICE: [u8; 32] = [4u8; 32];
623
pub const BOB: [u8; 32] = [5u8; 32];
624
pub const CHARLIE: [u8; 32] = [6u8; 32];
625
pub const DAVE: [u8; 32] = [7u8; 32];
626
pub const EVE: [u8; 32] = [8u8; 32];
627
pub const FERDIE: [u8; 32] = [9u8; 32];