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
#![cfg_attr(not(feature = "std"), no_std)]
18

            
19
#[cfg(test)]
20
mod mock;
21

            
22
use {
23
    cumulus_primitives_core::ParaId,
24
    frame_support::traits::Get,
25
    parity_scale_codec::Codec,
26
    sp_runtime::{traits::Zero, DigestItem},
27
    sp_std::{marker::PhantomData, vec::Vec},
28
};
29

            
30
#[cfg(feature = "runtime-benchmarks")]
31
use {nimbus_primitives::NimbusSignature, sp_consensus_aura::digests::CompatibleDigestItem};
32

            
33
sp_api::decl_runtime_apis! {
34
    /// API necessary for block authorship with Tanssi.
35
    pub trait TanssiAuthorityAssignmentApi<AuthorityId>
36
    where AuthorityId: Codec
37
    {
38
        /// Returns the authorities for a given paraId
39
        fn para_id_authorities(para_id: ParaId) -> Option<Vec<AuthorityId>>;
40

            
41
        /// Returns the paraId for which an authority is assigned (if any)
42
        fn check_para_id_assignment(authority: AuthorityId) -> Option<ParaId>;
43

            
44
        /// Return the paraId assigned to a given authority on the next session.
45
        /// On session boundary this returns the same as `check_para_id_assignment`.
46
        fn check_para_id_assignment_next_session(authority: AuthorityId) -> Option<ParaId>;
47
    }
48
}
49

            
50
pub struct OnTimestampSet<SlotBeacon, SlotDuration>(PhantomData<(SlotBeacon, SlotDuration)>);
51
impl<SlotBeacon, SlotDuration> frame_support::traits::OnTimestampSet<u64>
52
    for OnTimestampSet<SlotBeacon, SlotDuration>
53
where
54
    SlotBeacon: nimbus_primitives::SlotBeacon,
55
    SlotDuration: Get<u64>,
56
{
57
    fn on_timestamp_set(moment: u64) {
58
        assert!(
59
            !SlotDuration::get().is_zero(),
60
            "Slot duration cannot be zero."
61
        );
62

            
63
        let timestamp_slot = moment / SlotDuration::get();
64

            
65
        assert!(
66
            SlotBeacon::slot() as u64 == timestamp_slot,
67
            "Timestamp slot must match SlotBeacon slot"
68
        );
69
    }
70
}
71

            
72
pub struct NimbusLookUp;
73
impl nimbus_primitives::AccountLookup<nimbus_primitives::NimbusId> for NimbusLookUp {
74
    fn lookup_account(author: &nimbus_primitives::NimbusId) -> Option<nimbus_primitives::NimbusId> {
75
        Some(author.clone())
76
    }
77
}
78

            
79
pub struct AuraDigestSlotBeacon<ContainerRuntime>(PhantomData<ContainerRuntime>);
80
impl<ContainerRuntime> nimbus_primitives::SlotBeacon for AuraDigestSlotBeacon<ContainerRuntime>
81
where
82
    ContainerRuntime: frame_system::Config,
83
{
84
    fn slot() -> u32 {
85
        use sp_consensus_aura::{Slot, AURA_ENGINE_ID};
86

            
87
        let slot_from_digest = frame_system::Pallet::<ContainerRuntime>::digest()
88
            .convert_first(|item: &DigestItem| item.pre_runtime_try_to::<Slot>(&AURA_ENGINE_ID));
89

            
90
        #[cfg(not(feature = "runtime-benchmarks"))]
91
        let slot = slot_from_digest.expect("slot digest should exist");
92
        #[cfg(feature = "runtime-benchmarks")]
93
        let slot = slot_from_digest.unwrap_or_default();
94

            
95
        let slot: u64 = slot.into();
96
        slot as u32
97
    }
98
    #[cfg(feature = "runtime-benchmarks")]
99
    fn set_slot(slot: u32) {
100
        let aura_digest_item =
101
            <DigestItem as CompatibleDigestItem<NimbusSignature>>::aura_pre_digest(
102
                (slot as u64).into(),
103
            );
104
        frame_system::Pallet::<ContainerRuntime>::deposit_log(aura_digest_item);
105
    }
106
}
107

            
108
#[cfg(test)]
109
mod test {
110
    use {
111
        super::{AuraDigestSlotBeacon, NimbusLookUp, OnTimestampSet},
112
        crate::mock::{new_test_ext, Test},
113
        frame_support::traits::OnTimestampSet as OnTimestampSetT,
114
        nimbus_primitives::{AccountLookup, NimbusId, SlotBeacon},
115
        parity_scale_codec::Encode,
116
        sp_consensus_aura::AURA_ENGINE_ID,
117
        sp_core::ByteArray,
118
        sp_runtime::traits::ConstU64,
119
    };
120

            
121
    #[test]
122
    fn test_on_timestamp_set() {
123
        pub struct SlotBeaconMock {}
124
        impl SlotBeacon for SlotBeaconMock {
125
            fn slot() -> u32 {
126
                1
127
            }
128
        }
129

            
130
        OnTimestampSet::<SlotBeaconMock, ConstU64<1000>>::on_timestamp_set(1000u64);
131
    }
132

            
133
    #[test]
134
    fn test_nimbus_lookup() {
135
        let account = NimbusId::from_slice(&[0u8; 32]).unwrap();
136
        let lookup = NimbusLookUp::lookup_account(&account).expect("to be ok");
137

            
138
        assert!(account.eq(&lookup))
139
    }
140

            
141
    #[test]
142
    fn test_digest() {
143
        new_test_ext().execute_with(|| {
144
            frame_system::Pallet::<Test>::deposit_log(sp_runtime::generic::DigestItem::PreRuntime(
145
                AURA_ENGINE_ID,
146
                7u64.encode(),
147
            ));
148

            
149
            assert_eq!(AuraDigestSlotBeacon::<Test>::slot(), 7u32);
150
        });
151
    }
152
}