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
//! A collection of node-specific RPC methods.
18
//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer
19
//! used by Substrate nodes. This file extends those RPC definitions with
20
//! capabilities that are specific to this project's runtime configuration.
21

            
22
#![warn(missing_docs)]
23

            
24
use {
25
    cumulus_primitives_core::ParaId,
26
    dancebox_runtime::{opaque::Block, AccountId, Index as Nonce},
27
    frame_support::{CloneNoBound, DefaultNoBound},
28
    manual_xcm_rpc::{ManualXcm, ManualXcmApiServer},
29
    polkadot_primitives::Hash,
30
    sc_client_api::{AuxStore, UsageProvider},
31
    sc_consensus_manual_seal::{
32
        rpc::{ManualSeal, ManualSealApiServer},
33
        EngineCommand,
34
    },
35
    sc_transaction_pool_api::TransactionPool,
36
    sp_api::ProvideRuntimeApi,
37
    sp_block_builder::BlockBuilder,
38
    sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata},
39
    std::{marker::PhantomData, sync::Arc},
40
};
41

            
42
/// A type representing all RPC extensions.
43
pub type RpcExtension = jsonrpsee::RpcModule<()>;
44

            
45
/// Full client dependencies
46
pub struct FullDeps<C, P> {
47
    /// The client instance to use.
48
    pub client: Arc<C>,
49
    /// Transaction pool instance.
50
    pub pool: Arc<P>,
51
    /// Manual seal command sink
52
    pub command_sink: Option<futures::channel::mpsc::Sender<EngineCommand<Hash>>>,
53
    /// Channels for manual xcm messages (downward, hrmp)
54
    pub xcm_senders: Option<(flume::Sender<Vec<u8>>, flume::Sender<(ParaId, Vec<u8>)>)>,
55
}
56

            
57
tp_traits::alias!(
58
    /// Test
59
    pub trait SubstrateRpcRuntimeApi<Client : (sp_api::CallApiAt<Block>)>:
60
        sp_api::ConstructRuntimeApi<
61
            Block,
62
            Client,
63
            RuntimeApi:
64
                substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
65
                + BlockBuilder<Block>
66
        > + Send + Sync + 'static
67
);
68

            
69
/// Instantiate all RPC extensions.
70
pub fn create_full<C, P>(
71
    deps: FullDeps<C, P>,
72
) -> Result<RpcExtension, Box<dyn std::error::Error + Send + Sync>>
73
where
74
    C: ProvideRuntimeApi<Block>
75
        + HeaderBackend<Block>
76
        + AuxStore
77
        + HeaderMetadata<Block, Error = BlockChainError>
78
        + Send
79
        + Sync
80
        + UsageProvider<Block>
81
        + 'static,
82
    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
83
    C::Api: BlockBuilder<Block>,
84
    P: TransactionPool + Sync + Send + 'static,
85
{
86
    use substrate_frame_rpc_system::{System, SystemApiServer};
87

            
88
    let mut module = RpcExtension::new(());
89
    let FullDeps {
90
        client,
91
        pool,
92
        command_sink,
93
        xcm_senders,
94
    } = deps;
95

            
96
    module.merge(System::new(client.clone(), pool).into_rpc())?;
97

            
98
    if let Some(command_sink) = command_sink {
99
        module.merge(
100
            // We provide the rpc handler with the sending end of the channel to allow the rpc
101
            // send EngineCommands to the background block authorship task.
102
            ManualSeal::new(command_sink).into_rpc(),
103
        )?;
104
    };
105

            
106
    if let Some((downward_message_channel, hrmp_message_channel)) = xcm_senders {
107
        module.merge(
108
            ManualXcm {
109
                downward_message_channel,
110
                hrmp_message_channel,
111
            }
112
            .into_rpc(),
113
        )?;
114
    }
115

            
116
    Ok(module)
117
}
118

            
119
/// Contains the `GenerateRpcBuilder` trait and defines or re-exports all types it uses.
120
pub mod generate_rpc_builder {
121
    // We re-export types with specific type parameters, no need to be verbose documenting that.
122
    #![allow(missing_docs)]
123

            
124
    pub use {
125
        crate::service::{ContainerChainBackend, ContainerChainClient, MinimalContainerRuntimeApi},
126
        sc_service::{Error as ServiceError, TaskManager},
127
        std::sync::Arc,
128
        substrate_prometheus_endpoint::Registry as PrometheusRegistry,
129
        tc_consensus::ParaId,
130
    };
131

            
132
    // TODO: It would be better to use a container chain types.
133
    pub use dancebox_runtime::{opaque::Block, Hash};
134

            
135
    pub type SyncingService = sc_network_sync::SyncingService<Block>;
136
    pub type TransactionPool<RuntimeApi> =
137
        sc_transaction_pool::TransactionPoolHandle<Block, ContainerChainClient<RuntimeApi>>;
138
    pub type CommandSink =
139
        futures::channel::mpsc::Sender<sc_consensus_manual_seal::EngineCommand<Hash>>;
140
    pub type XcmSenders = (flume::Sender<Vec<u8>>, flume::Sender<(ParaId, Vec<u8>)>);
141
    pub type Network = dyn sc_network::service::traits::NetworkService;
142
    pub type CompleteRpcBuilder = Box<
143
        dyn Fn(sc_rpc::SubscriptionTaskExecutor) -> Result<jsonrpsee::RpcModule<()>, ServiceError>,
144
    >;
145

            
146
    pub struct GenerateRpcBuilderParams<'a, RuntimeApi: MinimalContainerRuntimeApi> {
147
        pub task_manager: &'a TaskManager,
148
        pub container_chain_config: &'a sc_service::Configuration,
149

            
150
        pub client: Arc<ContainerChainClient<RuntimeApi>>,
151
        pub backend: Arc<ContainerChainBackend>,
152
        pub sync_service: Arc<SyncingService>,
153
        pub transaction_pool: Arc<TransactionPool<RuntimeApi>>,
154
        pub prometheus_registry: Option<PrometheusRegistry>,
155
        pub command_sink: Option<CommandSink>,
156
        pub xcm_senders: Option<XcmSenders>,
157
        pub network: Arc<Network>,
158
    }
159

            
160
    pub trait GenerateRpcBuilder<RuntimeApi: MinimalContainerRuntimeApi>:
161
        Clone + Sync + Send
162
    {
163
        fn generate(
164
            &self,
165
            params: GenerateRpcBuilderParams<RuntimeApi>,
166
        ) -> Result<CompleteRpcBuilder, ServiceError>;
167
    }
168
}
169

            
170
/// Generate an rpc builder for simple substrate container chains.
171
#[derive(CloneNoBound, DefaultNoBound)]
172
pub struct GenerateSubstrateRpcBuilder<RuntimeApi>(pub PhantomData<RuntimeApi>);
173
impl<RuntimeApi> GenerateSubstrateRpcBuilder<RuntimeApi> {
174
    /// Creates a new instance.
175
    pub fn new() -> Self {
176
        Self(PhantomData)
177
    }
178
}
179

            
180
mod impl_generate_rpc_builder {
181
    use {super::*, generate_rpc_builder::*};
182

            
183
    impl<
184
            RuntimeApi: MinimalContainerRuntimeApi
185
                + crate::rpc::SubstrateRpcRuntimeApi<ContainerChainClient<RuntimeApi>>,
186
        > GenerateRpcBuilder<RuntimeApi> for GenerateSubstrateRpcBuilder<RuntimeApi>
187
    {
188
        fn generate(
189
            &self,
190
            GenerateRpcBuilderParams {
191
                client,
192
                transaction_pool,
193
                command_sink,
194
                xcm_senders,
195
                ..
196
            }: GenerateRpcBuilderParams<RuntimeApi>,
197
        ) -> Result<CompleteRpcBuilder, ServiceError> {
198
            let client = client.clone();
199
            let transaction_pool = transaction_pool.clone();
200

            
201
            Ok(Box::new(move |_| {
202
                let deps = FullDeps {
203
                    client: client.clone(),
204
                    pool: transaction_pool.clone(),
205
                    command_sink: command_sink.clone(),
206
                    xcm_senders: xcm_senders.clone(),
207
                };
208

            
209
                create_full(deps).map_err(Into::into)
210
            }))
211
        }
212
    }
213
}