1#![warn(missing_docs)]
21
22use futures::StreamExt;
23use log::warn;
24use std::sync::Arc;
25
26use jsonrpsee::{
27 core::{async_trait, server::PendingSubscriptionSink},
28 proc_macros::rpc,
29};
30
31mod error;
32mod finality;
33mod notification;
34mod report;
35
36use error::Error;
37use finality::{EncodedFinalityProof, RpcFinalityProofProvider};
38use notification::JustificationNotification;
39use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates};
40use sc_consensus_grandpa::GrandpaJustificationStream;
41use sc_rpc::{
42 utils::{BoundedVecDeque, PendingSubscription},
43 SubscriptionTaskExecutor,
44};
45use sp_runtime::traits::{Block as BlockT, NumberFor};
46
47#[rpc(client, server)]
49pub trait GrandpaApi<Notification, Hash, Number> {
50 #[method(name = "grandpa_roundState")]
53 async fn round_state(&self) -> Result<ReportedRoundStates, Error>;
54
55 #[subscription(
58 name = "grandpa_subscribeJustifications" => "grandpa_justifications",
59 unsubscribe = "grandpa_unsubscribeJustifications",
60 item = Notification
61 )]
62 fn subscribe_justifications(&self);
63
64 #[method(name = "grandpa_proveFinality")]
67 async fn prove_finality(&self, block: Number) -> Result<Option<EncodedFinalityProof>, Error>;
68}
69
70pub struct Grandpa<AuthoritySet, VoterState, Block: BlockT, ProofProvider> {
72 executor: SubscriptionTaskExecutor,
73 authority_set: AuthoritySet,
74 voter_state: VoterState,
75 justification_stream: GrandpaJustificationStream<Block>,
76 finality_proof_provider: Arc<ProofProvider>,
77}
78impl<AuthoritySet, VoterState, Block: BlockT, ProofProvider>
79 Grandpa<AuthoritySet, VoterState, Block, ProofProvider>
80{
81 pub fn new(
83 executor: SubscriptionTaskExecutor,
84 authority_set: AuthoritySet,
85 voter_state: VoterState,
86 justification_stream: GrandpaJustificationStream<Block>,
87 finality_proof_provider: Arc<ProofProvider>,
88 ) -> Self {
89 Self { executor, authority_set, voter_state, justification_stream, finality_proof_provider }
90 }
91}
92
93#[async_trait]
94impl<AuthoritySet, VoterState, Block, ProofProvider>
95 GrandpaApiServer<JustificationNotification, Block::Hash, NumberFor<Block>>
96 for Grandpa<AuthoritySet, VoterState, Block, ProofProvider>
97where
98 VoterState: ReportVoterState + Send + Sync + 'static,
99 AuthoritySet: ReportAuthoritySet + Send + Sync + 'static,
100 Block: BlockT,
101 ProofProvider: RpcFinalityProofProvider<Block> + Send + Sync + 'static,
102{
103 async fn round_state(&self) -> Result<ReportedRoundStates, Error> {
104 ReportedRoundStates::from(&self.authority_set, &self.voter_state)
105 }
106
107 fn subscribe_justifications(&self, pending: PendingSubscriptionSink) {
108 let stream = self.justification_stream.subscribe(100_000).map(
109 |x: sc_consensus_grandpa::GrandpaJustification<Block>| {
110 JustificationNotification::from(x)
111 },
112 );
113
114 sc_rpc::utils::spawn_subscription_task(
115 &self.executor,
116 PendingSubscription::from(pending).pipe_from_stream(stream, BoundedVecDeque::default()),
117 );
118 }
119
120 async fn prove_finality(
121 &self,
122 block: NumberFor<Block>,
123 ) -> Result<Option<EncodedFinalityProof>, Error> {
124 self.finality_proof_provider.rpc_prove_finality(block).map_err(|e| {
125 warn!("Error proving finality: {}", e);
126 error::Error::ProveFinalityFailed(e)
127 })
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use std::{collections::HashSet, sync::Arc};
135
136 use codec::{Decode, Encode};
137 use jsonrpsee::{core::EmptyServerParams as EmptyParams, types::SubscriptionId, RpcModule};
138 use sc_block_builder::BlockBuilderBuilder;
139 use sc_consensus_grandpa::{
140 report, AuthorityId, FinalityProof, GrandpaJustification, GrandpaJustificationSender,
141 };
142 use sc_rpc::testing::test_executor;
143 use sp_blockchain::HeaderBackend;
144 use sp_core::crypto::ByteArray;
145 use sp_keyring::Ed25519Keyring;
146 use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
147 use substrate_test_runtime_client::{
148 runtime::{Block, Header, H256},
149 DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
150 };
151
152 struct TestAuthoritySet;
153 struct TestVoterState;
154 struct EmptyVoterState;
155
156 struct TestFinalityProofProvider {
157 finality_proof: Option<FinalityProof<Header>>,
158 }
159
160 fn voters() -> HashSet<AuthorityId> {
161 let voter_id_1 = AuthorityId::from_slice(&[1; 32]).unwrap();
162 let voter_id_2 = AuthorityId::from_slice(&[2; 32]).unwrap();
163
164 vec![voter_id_1, voter_id_2].into_iter().collect()
165 }
166
167 impl ReportAuthoritySet for TestAuthoritySet {
168 fn get(&self) -> (u64, HashSet<AuthorityId>) {
169 (1, voters())
170 }
171 }
172
173 impl ReportVoterState for EmptyVoterState {
174 fn get(&self) -> Option<report::VoterState<AuthorityId>> {
175 None
176 }
177 }
178
179 fn header(number: u64) -> Header {
180 let parent_hash = match number {
181 0 => Default::default(),
182 _ => header(number - 1).hash(),
183 };
184 Header::new(
185 number,
186 H256::from_low_u64_be(0),
187 H256::from_low_u64_be(0),
188 parent_hash,
189 Default::default(),
190 )
191 }
192
193 impl<Block: BlockT> RpcFinalityProofProvider<Block> for TestFinalityProofProvider {
194 fn rpc_prove_finality(
195 &self,
196 _block: NumberFor<Block>,
197 ) -> Result<Option<EncodedFinalityProof>, sc_consensus_grandpa::FinalityProofError> {
198 Ok(Some(EncodedFinalityProof(
199 self.finality_proof
200 .as_ref()
201 .expect("Don't call rpc_prove_finality without setting the FinalityProof")
202 .encode()
203 .into(),
204 )))
205 }
206 }
207
208 impl ReportVoterState for TestVoterState {
209 fn get(&self) -> Option<report::VoterState<AuthorityId>> {
210 let voter_id_1 = AuthorityId::from_slice(&[1; 32]).unwrap();
211 let voters_best: HashSet<_> = vec![voter_id_1].into_iter().collect();
212
213 let best_round_state = sc_consensus_grandpa::report::RoundState {
214 total_weight: 100_u64.try_into().unwrap(),
215 threshold_weight: 67_u64.try_into().unwrap(),
216 prevote_current_weight: 50.into(),
217 prevote_ids: voters_best,
218 precommit_current_weight: 0.into(),
219 precommit_ids: HashSet::new(),
220 };
221
222 let past_round_state = sc_consensus_grandpa::report::RoundState {
223 total_weight: 100_u64.try_into().unwrap(),
224 threshold_weight: 67_u64.try_into().unwrap(),
225 prevote_current_weight: 100.into(),
226 prevote_ids: voters(),
227 precommit_current_weight: 100.into(),
228 precommit_ids: voters(),
229 };
230
231 let background_rounds = vec![(1, past_round_state)].into_iter().collect();
232
233 Some(report::VoterState { background_rounds, best_round: (2, best_round_state) })
234 }
235 }
236
237 fn setup_io_handler<VoterState>(
238 voter_state: VoterState,
239 ) -> (
240 RpcModule<Grandpa<TestAuthoritySet, VoterState, Block, TestFinalityProofProvider>>,
241 GrandpaJustificationSender<Block>,
242 )
243 where
244 VoterState: ReportVoterState + Send + Sync + 'static,
245 {
246 setup_io_handler_with_finality_proofs(voter_state, None)
247 }
248
249 fn setup_io_handler_with_finality_proofs<VoterState>(
250 voter_state: VoterState,
251 finality_proof: Option<FinalityProof<Header>>,
252 ) -> (
253 RpcModule<Grandpa<TestAuthoritySet, VoterState, Block, TestFinalityProofProvider>>,
254 GrandpaJustificationSender<Block>,
255 )
256 where
257 VoterState: ReportVoterState + Send + Sync + 'static,
258 {
259 let (justification_sender, justification_stream) = GrandpaJustificationStream::channel();
260 let finality_proof_provider = Arc::new(TestFinalityProofProvider { finality_proof });
261 let executor = test_executor();
262
263 let rpc = Grandpa::new(
264 executor,
265 TestAuthoritySet,
266 voter_state,
267 justification_stream,
268 finality_proof_provider,
269 )
270 .into_rpc();
271
272 (rpc, justification_sender)
273 }
274
275 #[tokio::test]
276 async fn uninitialized_rpc_handler() {
277 let (rpc, _) = setup_io_handler(EmptyVoterState);
278 let expected_response = r#"{"jsonrpc":"2.0","id":0,"error":{"code":1,"message":"GRANDPA RPC endpoint not ready"}}"#.to_string();
279 let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#;
280 let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap();
281
282 assert_eq!(expected_response, response);
283 }
284
285 #[tokio::test]
286 async fn working_rpc_handler() {
287 let (rpc, _) = setup_io_handler(TestVoterState);
288 let expected_response = "{\"jsonrpc\":\"2.0\",\"id\":0,\"result\":{\
289 \"setId\":1,\
290 \"best\":{\
291 \"round\":2,\"totalWeight\":100,\"thresholdWeight\":67,\
292 \"prevotes\":{\"currentWeight\":50,\"missing\":[\"5C7LYpP2ZH3tpKbvVvwiVe54AapxErdPBbvkYhe6y9ZBkqWt\"]},\
293 \"precommits\":{\"currentWeight\":0,\"missing\":[\"5C62Ck4UrFPiBtoCmeSrgF7x9yv9mn38446dhCpsi2mLHiFT\",\"5C7LYpP2ZH3tpKbvVvwiVe54AapxErdPBbvkYhe6y9ZBkqWt\"]}\
294 },\
295 \"background\":[{\
296 \"round\":1,\"totalWeight\":100,\"thresholdWeight\":67,\
297 \"prevotes\":{\"currentWeight\":100,\"missing\":[]},\
298 \"precommits\":{\"currentWeight\":100,\"missing\":[]}\
299 }]\
300 }}".to_string();
301
302 let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":0}"#;
303 let (response, _) = rpc.raw_json_request(&request, 1).await.unwrap();
304 assert_eq!(expected_response, response);
305 }
306
307 #[tokio::test]
308 async fn subscribe_and_unsubscribe_with_wrong_id() {
309 let (rpc, _) = setup_io_handler(TestVoterState);
310 let _sub = rpc
312 .subscribe_unbounded("grandpa_subscribeJustifications", EmptyParams::new())
313 .await
314 .unwrap();
315
316 let (response, _) = rpc
318 .raw_json_request(
319 r#"{"jsonrpc":"2.0","method":"grandpa_unsubscribeJustifications","params":["FOO"],"id":1}"#,
320 1,
321 )
322 .await
323 .unwrap();
324 let expected = r#"{"jsonrpc":"2.0","id":1,"result":false}"#;
325
326 assert_eq!(response, expected);
327 }
328
329 fn create_justification() -> GrandpaJustification<Block> {
330 let peers = &[Ed25519Keyring::Alice];
331
332 let builder = TestClientBuilder::new();
333 let client = builder.build();
334 let client = Arc::new(client);
335
336 let built_block = BlockBuilderBuilder::new(&*client)
337 .on_parent_block(client.info().best_hash)
338 .with_parent_block_number(client.info().best_number)
339 .build()
340 .unwrap()
341 .build()
342 .unwrap();
343
344 let block = built_block.block;
345 let block_hash = block.hash();
346
347 let justification = {
348 let round = 1;
349 let set_id = 0;
350
351 let precommit = finality_grandpa::Precommit {
352 target_hash: block_hash,
353 target_number: *block.header.number(),
354 };
355
356 let msg = finality_grandpa::Message::Precommit(precommit.clone());
357 let encoded = sp_consensus_grandpa::localized_payload(round, set_id, &msg);
358 let signature = peers[0].sign(&encoded[..]).into();
359
360 let precommit = finality_grandpa::SignedPrecommit {
361 precommit,
362 signature,
363 id: peers[0].public().into(),
364 };
365
366 let commit = finality_grandpa::Commit {
367 target_hash: block_hash,
368 target_number: *block.header.number(),
369 precommits: vec![precommit],
370 };
371
372 GrandpaJustification::from_commit(&client, round, commit).unwrap()
373 };
374
375 justification
376 }
377
378 #[tokio::test]
379 async fn subscribe_and_listen_to_one_justification() {
380 let (rpc, justification_sender) = setup_io_handler(TestVoterState);
381
382 let mut sub = rpc
383 .subscribe_unbounded("grandpa_subscribeJustifications", EmptyParams::new())
384 .await
385 .unwrap();
386
387 let justification = create_justification();
389 justification_sender.notify(|| Ok::<_, ()>(justification.clone())).unwrap();
390
391 let (recv_justification, recv_sub_id): (sp_core::Bytes, SubscriptionId) =
393 sub.next().await.unwrap().unwrap();
394 let recv_justification: GrandpaJustification<Block> =
395 Decode::decode(&mut &recv_justification[..]).unwrap();
396
397 assert_eq!(&recv_sub_id, sub.subscription_id());
398 assert_eq!(recv_justification, justification);
399 }
400
401 #[tokio::test]
402 async fn prove_finality_with_test_finality_proof_provider() {
403 let finality_proof = FinalityProof {
404 block: header(42).hash(),
405 justification: create_justification().encode(),
406 unknown_headers: vec![header(2)],
407 };
408 let (rpc, _) =
409 setup_io_handler_with_finality_proofs(TestVoterState, Some(finality_proof.clone()));
410
411 let bytes: sp_core::Bytes = rpc.call("grandpa_proveFinality", [42]).await.unwrap();
412 let finality_proof_rpc: FinalityProof<Header> = Decode::decode(&mut &bytes[..]).unwrap();
413 assert_eq!(finality_proof_rpc, finality_proof);
414 }
415}