cuprated/rpc/
rpc_handler.rs

1//! `cuprated`'s implementation of [`RpcHandler`].
2
3use std::task::{Context, Poll};
4
5use anyhow::Error;
6use futures::future::BoxFuture;
7use monero_serai::block::Block;
8use tower::Service;
9
10use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle};
11use cuprate_consensus::BlockchainContextService;
12use cuprate_pruning::PruningSeed;
13use cuprate_rpc_interface::RpcHandler;
14use cuprate_rpc_types::{
15    bin::{BinRequest, BinResponse},
16    json::{JsonRpcRequest, JsonRpcResponse},
17    other::{OtherRequest, OtherResponse},
18};
19use cuprate_txpool::service::TxpoolReadHandle;
20use cuprate_types::BlockTemplate;
21
22use crate::rpc::handlers;
23
24/// TODO: use real type when public.
25#[derive(Clone)]
26pub enum BlockchainManagerRequest {
27    /// Pop blocks off the top of the blockchain.
28    ///
29    /// Input is the amount of blocks to pop.
30    PopBlocks { amount: usize },
31
32    /// Start pruning the blockchain.
33    Prune,
34
35    /// Is the blockchain pruned?
36    Pruned,
37
38    /// Relay a block to the network.
39    RelayBlock(
40        /// This is [`Box`]ed due to `clippy::large_enum_variant`.
41        Box<Block>,
42    ),
43
44    /// Sync/flush the blockchain database to disk.
45    Sync,
46
47    /// Is the blockchain in the middle of syncing?
48    ///
49    /// This returning `false` does not necessarily
50    /// mean [`BlockchainManagerRequest::Synced`] will
51    /// return `true`, for example, if the network has been
52    /// cut off and we have no peers, this will return `false`,
53    /// however, [`BlockchainManagerRequest::Synced`] may return
54    /// `true` if the latest known chain tip is equal to our height.
55    Syncing,
56
57    /// Is the blockchain fully synced?
58    Synced,
59
60    /// Current target block time.
61    Target,
62
63    /// The height of the next block in the chain.
64    TargetHeight,
65
66    /// Generate new blocks.
67    ///
68    /// This request is only for regtest, see RPC's `generateblocks`.
69    GenerateBlocks {
70        /// Number of the blocks to be generated.
71        amount_of_blocks: u64,
72        /// The previous block's hash.
73        prev_block: Option<[u8; 32]>,
74        /// The starting value for the nonce.
75        starting_nonce: u32,
76        /// The address that will receive the coinbase reward.
77        wallet_address: String,
78    },
79
80    //    // TODO: the below requests actually belong to the block downloader/syncer:
81    //    // <https://github.com/Cuprate/cuprate/pull/320#discussion_r1811089758>
82    //    /// Get [`Span`] data.
83    //    ///
84    //    /// This is data that describes an active downloading process,
85    //    /// if we are fully synced, this will return an empty [`Vec`].
86    //    Spans,
87
88    //
89    /// Get the next [`PruningSeed`] needed for a pruned sync.
90    NextNeededPruningSeed,
91
92    /// Create a block template.
93    CreateBlockTemplate {
94        prev_block: [u8; 32],
95        account_public_address: String,
96        extra_nonce: Vec<u8>,
97    },
98
99    /// Safely shutdown `cuprated`.
100    Stop,
101}
102
103/// TODO: use real type when public.
104#[derive(Clone)]
105pub enum BlockchainManagerResponse {
106    /// General OK response.
107    ///
108    /// Response to:
109    /// - [`BlockchainManagerRequest::Prune`]
110    /// - [`BlockchainManagerRequest::RelayBlock`]
111    /// - [`BlockchainManagerRequest::Sync`]
112    Ok,
113
114    /// Response to [`BlockchainManagerRequest::PopBlocks`]
115    PopBlocks { new_height: usize },
116
117    /// Response to [`BlockchainManagerRequest::Prune`]
118    Prune(PruningSeed),
119
120    /// Response to [`BlockchainManagerRequest::Pruned`]
121    Pruned(bool),
122
123    /// Response to [`BlockchainManagerRequest::Syncing`]
124    Syncing(bool),
125
126    /// Response to [`BlockchainManagerRequest::Synced`]
127    Synced(bool),
128
129    /// Response to [`BlockchainManagerRequest::Target`]
130    Target(std::time::Duration),
131
132    /// Response to [`BlockchainManagerRequest::TargetHeight`]
133    TargetHeight { height: usize },
134
135    /// Response to [`BlockchainManagerRequest::GenerateBlocks`]
136    GenerateBlocks {
137        /// Hashes of the blocks generated.
138        blocks: Vec<[u8; 32]>,
139        /// The new top height. (TODO: is this correct?)
140        height: usize,
141    },
142
143    /// Response to [`BlockchainManagerRequest::NextNeededPruningSeed`].
144    NextNeededPruningSeed(PruningSeed),
145
146    /// Response to [`BlockchainManagerRequest::CreateBlockTemplate`].
147    CreateBlockTemplate(Box<BlockTemplate>),
148}
149
150/// TODO: use real type when public.
151pub type BlockchainManagerHandle = cuprate_database_service::DatabaseReadService<
152    BlockchainManagerRequest,
153    BlockchainManagerResponse,
154>;
155
156/// cuprated's RPC handler service.
157#[derive(Clone)]
158pub struct CupratedRpcHandler {
159    /// Should this RPC server be [restricted](RpcHandler::is_restricted)?
160    ///
161    /// This is not `pub` on purpose, as it should not be mutated after [`Self::new`].
162    restricted: bool,
163
164    /// Read handle to the blockchain database.
165    pub blockchain_read: BlockchainReadHandle,
166
167    /// Handle to the blockchain context service.
168    pub blockchain_context: BlockchainContextService,
169
170    /// Read handle to the transaction pool database.
171    pub txpool_read: TxpoolReadHandle,
172    // TODO: handle to txpool service.
173}
174
175impl CupratedRpcHandler {
176    /// Create a new [`Self`].
177    pub const fn new(
178        restricted: bool,
179        blockchain_read: BlockchainReadHandle,
180        blockchain_context: BlockchainContextService,
181        txpool_read: TxpoolReadHandle,
182    ) -> Self {
183        Self {
184            restricted,
185            blockchain_read,
186            blockchain_context,
187            txpool_read,
188        }
189    }
190}
191
192impl RpcHandler for CupratedRpcHandler {
193    fn is_restricted(&self) -> bool {
194        self.restricted
195    }
196}
197
198impl Service<JsonRpcRequest> for CupratedRpcHandler {
199    type Response = JsonRpcResponse;
200    type Error = Error;
201    type Future = BoxFuture<'static, Result<JsonRpcResponse, Error>>;
202
203    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
204        Poll::Ready(Ok(()))
205    }
206
207    fn call(&mut self, request: JsonRpcRequest) -> Self::Future {
208        let state = self.clone();
209        Box::pin(handlers::json_rpc::map_request(state, request))
210    }
211}
212
213impl Service<BinRequest> for CupratedRpcHandler {
214    type Response = BinResponse;
215    type Error = Error;
216    type Future = BoxFuture<'static, Result<BinResponse, Error>>;
217
218    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
219        Poll::Ready(Ok(()))
220    }
221
222    fn call(&mut self, request: BinRequest) -> Self::Future {
223        let state = self.clone();
224        Box::pin(handlers::bin::map_request(state, request))
225    }
226}
227
228impl Service<OtherRequest> for CupratedRpcHandler {
229    type Response = OtherResponse;
230    type Error = Error;
231    type Future = BoxFuture<'static, Result<OtherResponse, Error>>;
232
233    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
234        Poll::Ready(Ok(()))
235    }
236
237    fn call(&mut self, request: OtherRequest) -> Self::Future {
238        let state = self.clone();
239        Box::pin(handlers::other_json::map_request(state, request))
240    }
241}