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
//! The Manual Seal implementation for the OrchestratorAuraConsensus
18

            
19
use {
20
    cumulus_primitives_core::ParaId,
21
    dp_consensus::TanssiAuthorityAssignmentApi,
22
    nimbus_primitives::{
23
        CompatibleDigestItem as NimbusCompatibleDigestItem, NimbusId, NimbusPair, NimbusSignature,
24
    },
25
    sc_client_api::{AuxStore, UsageProvider},
26
    sc_consensus::BlockImportParams,
27
    sc_consensus_manual_seal::{ConsensusDataProvider, Error},
28
    sp_api::{ProvideRuntimeApi, StorageProof},
29
    sp_blockchain::{HeaderBackend, HeaderMetadata},
30
    sp_consensus_aura::{digests::CompatibleDigestItem, AuraApi, Slot, SlotDuration},
31
    sp_core::Pair,
32
    sp_inherents::InherentData,
33
    sp_keystore::KeystorePtr,
34
    sp_runtime::{
35
        traits::{Block as BlockT, Header as HeaderT},
36
        Digest, DigestItem,
37
    },
38
    sp_timestamp::TimestampInherentData,
39
    std::{marker::PhantomData, sync::Arc},
40
};
41
/// Consensus data provider for Orchestrator Manual Seal Aura.
42
pub struct OrchestratorManualSealAuraConsensusDataProvider<B, C, P> {
43
    // slot duration
44
    slot_duration: SlotDuration,
45
    /// Shared reference to keystore
46
    pub keystore: KeystorePtr,
47

            
48
    /// Shared reference to the client
49
    pub client: Arc<C>,
50

            
51
    /// ParaId of the orchestrator
52
    pub orchestrator_para_id: ParaId,
53

            
54
    // phantom data for required generics
55
    _phantom: PhantomData<(B, C, P)>,
56
}
57

            
58
impl<B, C, P> OrchestratorManualSealAuraConsensusDataProvider<B, C, P>
59
where
60
    B: BlockT,
61
    C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B>,
62
    C::Api: AuraApi<B, nimbus_primitives::NimbusId>,
63
{
64
    /// Creates a new instance of the [`AuraConsensusDataProvider`], requires that `client`
65
    /// implements [`sp_consensus_aura::AuraApi`]
66
194
    pub fn new(client: Arc<C>, keystore: KeystorePtr, orchestrator_para_id: ParaId) -> Self {
67
194
        let slot_duration = sc_consensus_aura::slot_duration(&*client)
68
194
            .expect("slot_duration is always present; qed.");
69
194

            
70
194
        Self {
71
194
            slot_duration,
72
194
            keystore,
73
194
            client,
74
194
            orchestrator_para_id,
75
194
            _phantom: PhantomData,
76
194
        }
77
194
    }
78
}
79
impl<B, C, P> ConsensusDataProvider<B> for OrchestratorManualSealAuraConsensusDataProvider<B, C, P>
80
where
81
    B: BlockT,
82
    C: AuxStore
83
        + HeaderBackend<B>
84
        + HeaderMetadata<B, Error = sp_blockchain::Error>
85
        + UsageProvider<B>
86
        + ProvideRuntimeApi<B>,
87
    C::Api: TanssiAuthorityAssignmentApi<B, nimbus_primitives::NimbusId>,
88
    P: Send + Sync,
89
{
90
    type Proof = P;
91

            
92
7810
    fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<Digest, Error> {
93
7810
        let timestamp = inherents
94
7810
            .timestamp_inherent_data()?
95
7810
            .expect("Timestamp is always present; qed");
96
7810

            
97
7810
        // we always calculate the new slot number based on the current time-stamp and the slot
98
7810
        // duration.
99
7810
        // TODO: we need to add the nimbus digest here
100
7810
        let slot = Slot::from_timestamp(timestamp, self.slot_duration);
101
7810
        let aura_digest_item =
102
7810
            <DigestItem as CompatibleDigestItem<NimbusSignature>>::aura_pre_digest(slot);
103

            
104
        // Fetch the authorities for the orchestrator chain
105
7810
        let authorities = self
106
7810
            .client
107
7810
            .runtime_api()
108
7810
            .para_id_authorities(parent.hash(), self.orchestrator_para_id)
109
7810
            .ok()
110
7810
            .ok_or(sp_consensus::Error::InvalidAuthoritiesSet)?
111
7810
            .unwrap_or_default();
112
7810

            
113
7810
        let expected_author = crate::slot_author::<NimbusPair>(slot, authorities.as_ref());
114

            
115
        // TODO: this should always be included, but breaks manual seal tests. We should modify
116
        // once configuration on how manual seal changes
117
7810
        let digest = if let Some(author) = expected_author {
118
7810
            let nimbus_digest =
119
7810
                <DigestItem as NimbusCompatibleDigestItem>::nimbus_pre_digest(author.clone());
120
7810
            Digest {
121
7810
                logs: vec![aura_digest_item, nimbus_digest],
122
7810
            }
123
        } else {
124
            Digest {
125
                logs: vec![aura_digest_item],
126
            }
127
        };
128
7810
        Ok(digest)
129
7810
    }
130

            
131
7810
    fn append_block_import(
132
7810
        &self,
133
7810
        _parent: &B::Header,
134
7810
        _params: &mut BlockImportParams<B>,
135
7810
        _inherents: &InherentData,
136
7810
        _proof: Self::Proof,
137
7810
    ) -> Result<(), Error> {
138
7810
        Ok(())
139
7810
    }
140
}
141

            
142
/// Helper function to generate a crypto pair from seed
143
11622
pub fn get_aura_id_from_seed(seed: &str) -> NimbusId {
144
11622
    sp_core::sr25519::Pair::from_string(&format!("//{}", seed), None)
145
11622
        .expect("static values are valid; qed")
146
11622
        .public()
147
11622
        .into()
148
11622
}
149

            
150
/// Consensus data provider for Container Manual Seal Aura.
151
pub struct ContainerManualSealAuraConsensusDataProvider<B> {
152
    // slot duration
153
    slot_duration: SlotDuration,
154
    // Authorities from which the author should be calculated
155
    pub authorities: Vec<NimbusId>,
156
    // phantom data for required generics
157
    _phantom: PhantomData<B>,
158
}
159

            
160
impl<B> ContainerManualSealAuraConsensusDataProvider<B>
161
where
162
    B: BlockT,
163
{
164
    /// Creates a new instance of the [`AuraConsensusDataProvider`], requires that `client`
165
    /// implements [`sp_consensus_aura::AuraApi`]
166
510
    pub fn new(slot_duration: SlotDuration, authorities: Vec<NimbusId>) -> Self {
167
510
        Self {
168
510
            slot_duration,
169
510
            authorities,
170
510
            _phantom: PhantomData,
171
510
        }
172
510
    }
173
}
174
impl<B> ConsensusDataProvider<B> for ContainerManualSealAuraConsensusDataProvider<B>
175
where
176
    B: BlockT,
177
{
178
    type Proof = StorageProof;
179

            
180
3578
    fn create_digest(
181
3578
        &self,
182
3578
        _parent: &B::Header,
183
3578
        inherents: &InherentData,
184
3578
    ) -> Result<Digest, Error> {
185
3578
        let timestamp = inherents
186
3578
            .timestamp_inherent_data()?
187
3578
            .expect("Timestamp is always present; qed");
188
3578

            
189
3578
        // we always calculate the new slot number based on the current time-stamp and the slot
190
3578
        // duration.
191
3578
        // TODO: we need to add the nimbus digest here
192
3578
        let slot = Slot::from_timestamp(timestamp, self.slot_duration);
193
3578
        let aura_digest_item =
194
3578
            <DigestItem as CompatibleDigestItem<NimbusSignature>>::aura_pre_digest(slot);
195
3578

            
196
3578
        let alice_id = get_aura_id_from_seed("alice");
197
3578
        let expected_author: Option<nimbus_primitives::NimbusId> = Some(alice_id);
198

            
199
        // TODO: this should always be included, but breaks manual seal tests. We should modify
200
        // once configuration on how manual seal changes
201
3578
        let digest = if let Some(author) = expected_author {
202
3578
            let nimbus_digest =
203
3578
                <DigestItem as NimbusCompatibleDigestItem>::nimbus_pre_digest(author);
204
3578
            Digest {
205
3578
                logs: vec![aura_digest_item, nimbus_digest],
206
3578
            }
207
        } else {
208
            Digest {
209
                logs: vec![aura_digest_item],
210
            }
211
        };
212
3578
        Ok(digest)
213
3578
    }
214

            
215
3574
    fn append_block_import(
216
3574
        &self,
217
3574
        _parent: &B::Header,
218
3574
        _params: &mut BlockImportParams<B>,
219
3574
        _inherents: &InherentData,
220
3574
        _proof: Self::Proof,
221
3574
    ) -> Result<(), Error> {
222
3574
        Ok(())
223
3574
    }
224
}
225

            
226
impl<B> fc_rpc::pending::ConsensusDataProvider<B>
227
    for ContainerManualSealAuraConsensusDataProvider<B>
228
where
229
    B: BlockT,
230
{
231
4
    fn create_digest(
232
4
        &self,
233
4
        _parent: &B::Header,
234
4
        inherents: &InherentData,
235
4
    ) -> Result<sp_runtime::Digest, sp_inherents::Error> {
236
4
        <Self as ConsensusDataProvider<B>>::create_digest(self, _parent, inherents)
237
4
            .map_err(|_| sp_inherents::Error::FatalErrorReported)
238
4
    }
239
}