1+ // This file is part of Substrate.
2+
3+ // Copyright (C) 2020 Parity Technologies (UK) Ltd.
4+ // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5+
6+ // This program is free software: you can redistribute it and/or modify
7+ // it under the terms of the GNU General Public License as published by
8+ // the Free Software Foundation, either version 3 of the License, or
9+ // (at your option) any later version.
10+
11+ // This program is distributed in the hope that it will be useful,
12+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ // GNU General Public License for more details.
15+
16+ // You should have received a copy of the GNU General Public License
17+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
19+ //! BABE consensus data provider
20+
21+ use super :: ConsensusDataProvider ;
22+ use crate :: Error ;
23+
24+ use std:: {
25+ any:: Any ,
26+ borrow:: Cow ,
27+ sync:: { Arc , atomic} ,
28+ time:: SystemTime ,
29+ } ;
30+ use sc_client_api:: AuxStore ;
31+ use sc_consensus_babe:: {
32+ Config , Epoch , authorship, CompatibleDigestItem , BabeIntermediate ,
33+ register_babe_inherent_data_provider, INTERMEDIATE_KEY ,
34+ } ;
35+ use sc_consensus_epochs:: { SharedEpochChanges , descendent_query} ;
36+ use sc_keystore:: KeyStorePtr ;
37+
38+ use sp_api:: { ProvideRuntimeApi , TransactionFor } ;
39+ use sp_blockchain:: { HeaderBackend , HeaderMetadata } ;
40+ use sp_consensus:: BlockImportParams ;
41+ use sp_consensus_babe:: { BabeApi , inherents:: BabeInherentData } ;
42+ use sp_inherents:: { InherentDataProviders , InherentData , ProvideInherentData , InherentIdentifier } ;
43+ use sp_runtime:: {
44+ traits:: { DigestItemFor , DigestFor , Block as BlockT , Header as _} ,
45+ generic:: Digest ,
46+ } ;
47+ use sp_timestamp:: { InherentType , InherentError , INHERENT_IDENTIFIER } ;
48+
49+ /// Provides BABE-compatible predigests and BlockImportParams.
50+ /// Intended for use with BABE runtimes.
51+ pub struct BabeConsensusDataProvider < B : BlockT , C > {
52+ /// shared reference to keystore
53+ keystore : KeyStorePtr ,
54+
55+ /// Shared reference to the client.
56+ client : Arc < C > ,
57+
58+ /// Shared epoch changes
59+ epoch_changes : SharedEpochChanges < B , Epoch > ,
60+
61+ /// BABE config, gotten from the runtime.
62+ config : Config ,
63+ }
64+
65+ impl < B , C > BabeConsensusDataProvider < B , C >
66+ where
67+ B : BlockT ,
68+ C : AuxStore + ProvideRuntimeApi < B > ,
69+ C :: Api : BabeApi < B , Error = sp_blockchain:: Error > ,
70+ {
71+ pub fn new (
72+ client : Arc < C > ,
73+ keystore : KeyStorePtr ,
74+ provider : & InherentDataProviders ,
75+ epoch_changes : SharedEpochChanges < B , Epoch > ,
76+ ) -> Result < Self , Error > {
77+ let config = Config :: get_or_compute ( & * client) ?;
78+ let timestamp_provider = SlotTimestampProvider :: new ( config. slot_duration ) ?;
79+
80+ provider. register_provider ( timestamp_provider) ?;
81+ register_babe_inherent_data_provider ( provider, config. slot_duration ) ?;
82+
83+ Ok ( Self {
84+ config,
85+ client,
86+ keystore,
87+ epoch_changes,
88+ } )
89+ }
90+ }
91+
92+ impl < B , C > ConsensusDataProvider < B > for BabeConsensusDataProvider < B , C >
93+ where
94+ B : BlockT ,
95+ C : AuxStore + HeaderBackend < B > + HeaderMetadata < B , Error = sp_blockchain:: Error > + ProvideRuntimeApi < B > ,
96+ C :: Api : BabeApi < B , Error = sp_blockchain:: Error > ,
97+ {
98+ type Transaction = TransactionFor < C , B > ;
99+
100+ fn create_digest ( & self , parent : & B :: Header , inherents : & InherentData ) -> Result < DigestFor < B > , Error > {
101+ let slot_number = inherents. babe_inherent_data ( ) ?;
102+
103+ let epoch_changes = self . epoch_changes . lock ( ) ;
104+ let epoch_descriptor = epoch_changes
105+ . epoch_descriptor_for_child_of (
106+ descendent_query ( & * self . client ) ,
107+ & parent. hash ( ) ,
108+ parent. number ( ) . clone ( ) ,
109+ slot_number,
110+ )
111+ . map_err ( |e| Error :: StringError ( format ! ( "failed to fetch epoch_descriptor: {}" , e) ) ) ?
112+ . ok_or_else ( || sp_consensus:: Error :: InvalidAuthoritiesSet ) ?;
113+
114+ let epoch = epoch_changes
115+ . viable_epoch (
116+ & epoch_descriptor,
117+ |slot| Epoch :: genesis ( & self . config , slot) ,
118+ )
119+ . ok_or_else ( || {
120+ log:: info!( target: "babe" , "create_digest: no viable_epoch :(" ) ;
121+ sp_consensus:: Error :: InvalidAuthoritiesSet
122+ } ) ?;
123+
124+ // this is a dev node environment, we should always be able to claim a slot.
125+ let ( predigest, _) = authorship:: claim_slot ( slot_number, epoch. as_ref ( ) , & self . keystore )
126+ . ok_or_else ( || Error :: StringError ( "failed to claim slot for authorship" . into ( ) ) ) ?;
127+
128+ Ok ( Digest {
129+ logs : vec ! [
130+ <DigestItemFor <B > as CompatibleDigestItem >:: babe_pre_digest( predigest) ,
131+ ] ,
132+ } )
133+ }
134+
135+ fn append_block_import (
136+ & self ,
137+ parent : & B :: Header ,
138+ params : & mut BlockImportParams < B , Self :: Transaction > ,
139+ inherents : & InherentData
140+ ) -> Result < ( ) , Error > {
141+ let slot_number = inherents. babe_inherent_data ( ) ?;
142+
143+ let epoch_descriptor = self . epoch_changes . lock ( )
144+ . epoch_descriptor_for_child_of (
145+ descendent_query ( & * self . client ) ,
146+ & parent. hash ( ) ,
147+ parent. number ( ) . clone ( ) ,
148+ slot_number,
149+ )
150+ . map_err ( |e| Error :: StringError ( format ! ( "failed to fetch epoch data: {}" , e) ) ) ?
151+ . ok_or_else ( || sp_consensus:: Error :: InvalidAuthoritiesSet ) ?;
152+
153+ params. intermediates . insert (
154+ Cow :: from ( INTERMEDIATE_KEY ) ,
155+ Box :: new ( BabeIntermediate :: < B > { epoch_descriptor } ) as Box < dyn Any > ,
156+ ) ;
157+
158+ Ok ( ( ) )
159+ }
160+ }
161+
162+ /// Provide duration since unix epoch in millisecond for timestamp inherent.
163+ /// Mocks the timestamp inherent to always produce the timestamp for the next babe slot.
164+ struct SlotTimestampProvider {
165+ time : atomic:: AtomicU64 ,
166+ slot_duration : u64
167+ }
168+
169+ impl SlotTimestampProvider {
170+ /// create a new mocked time stamp provider.
171+ fn new ( slot_duration : u64 ) -> Result < Self , Error > {
172+ let now = SystemTime :: now ( ) ;
173+ let duration = now. duration_since ( SystemTime :: UNIX_EPOCH )
174+ . map_err ( |err| Error :: StringError ( format ! ( "{}" , err) ) ) ?;
175+ Ok ( Self {
176+ time : atomic:: AtomicU64 :: new ( duration. as_millis ( ) as u64 ) ,
177+ slot_duration,
178+ } )
179+ }
180+ }
181+
182+ impl ProvideInherentData for SlotTimestampProvider {
183+ fn inherent_identifier ( & self ) -> & ' static InherentIdentifier {
184+ & INHERENT_IDENTIFIER
185+ }
186+
187+ fn provide_inherent_data ( & self , inherent_data : & mut InherentData ) -> Result < ( ) , sp_inherents:: Error > {
188+ // we update the time here.
189+ let duration: InherentType = self . time . fetch_add ( self . slot_duration , atomic:: Ordering :: SeqCst ) ;
190+ inherent_data. put_data ( INHERENT_IDENTIFIER , & duration) ?;
191+ Ok ( ( ) )
192+ }
193+
194+ fn error_to_string ( & self , error : & [ u8 ] ) -> Option < String > {
195+ InherentError :: try_from ( & INHERENT_IDENTIFIER , error) . map ( |e| format ! ( "{:?}" , e) )
196+ }
197+ }
0 commit comments