Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/full-node/src/json_rpc_service/requests_handler.rs
Line
Count
Source (jump to first uncovered line)
1
// Smoldot
2
// Copyright (C) 2023  Pierre Krieger
3
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
15
// You should have received a copy of the GNU General Public License
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
use futures_lite::future;
19
use smol::stream::StreamExt as _;
20
use smoldot::{
21
    executor,
22
    json_rpc::{methods, parse, service},
23
    trie,
24
};
25
use std::{
26
    future::Future,
27
    iter,
28
    pin::{self, Pin},
29
    sync::Arc,
30
};
31
32
use crate::{
33
    consensus_service, database_thread,
34
    json_rpc_service::{legacy_api_subscriptions, runtime_caches_service},
35
    network_service, LogCallback, LogLevel,
36
};
37
38
pub struct Config {
39
    /// Function that can be used to spawn background tasks.
40
    ///
41
    /// The tasks passed as parameter must be executed until they shut down.
42
    pub tasks_executor: Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send + Sync>,
43
44
    /// Function called in order to notify of something.
45
    pub log_callback: Arc<dyn LogCallback + Send + Sync>,
46
47
    pub receiver: async_channel::Receiver<Message>,
48
49
    /// Database to access blocks.
50
    pub database: Arc<database_thread::DatabaseThread>,
51
52
    /// Access to the network, and identifier of the chain from the point of view of the network
53
    /// service.
54
    pub network_service: (
55
        Arc<network_service::NetworkService>,
56
        network_service::ChainId,
57
    ),
58
59
    /// Name of the chain, as found in the chain specification.
60
    pub chain_name: String,
61
62
    /// Type of the chain, as found in the chain specification.
63
    pub chain_type: String,
64
65
    /// JSON-encoded properties of the chain, as found in the chain specification.
66
    pub chain_properties_json: String,
67
68
    /// Whether the chain is a live network. Found in the chain specification.
69
    pub chain_is_live: bool,
70
71
    /// Hash of the genesis block.
72
    // TODO: load from database maybe?
73
    pub genesis_block_hash: [u8; 32],
74
75
    /// Consensus service of the chain.
76
    pub consensus_service: Arc<consensus_service::ConsensusService>,
77
78
    /// Runtime caches service of the JSON-RPC service.
79
    pub runtime_caches_service: Arc<runtime_caches_service::RuntimeCachesService>,
80
}
81
82
pub enum Message {
83
    Request(service::RequestProcess),
84
    SubscriptionStart(service::SubscriptionStartProcess),
85
}
86
87
672
pub fn spawn_requests_handler(config: Config) {
88
672
    let tasks_executor = config.tasks_executor.clone();
89
672
    tasks_executor(Box::pin(async move {
90
672
        let mut receiver = pin::pin!(config.receiver);
91
        loop {
92
1.28k
            match 
receiver.next()695
.await {
93
23
                Some(Message::Request(request)) => match request.request() {
94
0
                    methods::MethodCall::rpc_methods {} => {
95
0
                        request.respond(methods::Response::rpc_methods(methods::RpcMethods {
96
0
                            methods: methods::MethodCall::method_names()
97
0
                                .map(|n| n.into())
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler00B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler00B9_
98
0
                                .collect(),
99
0
                        }));
100
0
                    }
101
102
1
                    methods::MethodCall::chainSpec_v1_chainName {} => {
103
1
                        request.respond(methods::Response::chainSpec_v1_chainName(
104
1
                            (&config.chain_name).into(),
105
1
                        ));
106
1
                    }
107
1
                    methods::MethodCall::chainSpec_v1_genesisHash {} => {
108
1
                        request.respond(methods::Response::chainSpec_v1_genesisHash(
109
1
                            methods::HashHexString(config.genesis_block_hash),
110
1
                        ));
111
1
                    }
112
1
                    methods::MethodCall::chainSpec_v1_properties {} => {
113
1
                        request.respond(methods::Response::chainSpec_v1_properties(
114
1
                            serde_json::from_str(&config.chain_properties_json).unwrap(),
115
1
                        ));
116
1
                    }
117
118
                    methods::MethodCall::chain_getBlockHash { height: Some(0) } => {
119
                        // In the case where the database was populated through a warp sync, it
120
                        // might not store block 0 in it. However, the hash of block 0 is
121
                        // particularly important for JSON-RPC clients, and as such we make sure
122
                        // to always respond successfully to block 0 requests, even if it isn't
123
                        // in the database.
124
1
                        request.respond(methods::Response::chain_getBlockHash(
125
1
                            methods::HashHexString(config.genesis_block_hash),
126
1
                        ))
127
                    }
128
1
                    methods::MethodCall::chain_getBlockHash { height } => {
129
1
                        let outcome = config
130
1
                            .database
131
1
                            .with_database(move |database| match height {
132
1
                                Some(height) => database.best_block_hash_by_number(height),
133
0
                                None => database.best_block_hash().map(Some),
134
1
                            })
_RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s_0B9_
Line
Count
Source
131
1
                            .with_database(move |database| match height {
132
1
                                Some(height) => database.best_block_hash_by_number(height),
133
0
                                None => database.best_block_hash().map(Some),
134
1
                            })
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s_0B9_
135
1
                            .await;
136
1
                        match outcome {
137
0
                            Ok(Some(hash)) => request.respond(
138
0
                                methods::Response::chain_getBlockHash(methods::HashHexString(hash)),
139
0
                            ),
140
1
                            Ok(None) => request.respond_null(),
141
0
                            Err(error) => {
142
0
                                config.log_callback.log(LogLevel::Warn, format!("json-rpc; request=chain_getBlockHash; height={:?}; database_error={}", height, error));
143
0
                                request.fail(parse::ErrorResponse::InternalError)
144
                            }
145
                        }
146
                    }
147
3
                    methods::MethodCall::chain_getHeader { hash } => {
148
3
                        let hash = match hash {
149
2
                            Some(h) => h.0,
150
1
                            None => match config
151
1
                                .database
152
1
                                .with_database(|db| db.best_block_hash())
_RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s0_0B9_
Line
Count
Source
152
1
                                .with_database(|db| db.best_block_hash())
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s0_0B9_
153
1
                                .await
154
                            {
155
1
                                Ok(b) => b,
156
                                Err(_) => {
157
0
                                    request.fail(service::ErrorResponse::InternalError);
158
0
                                    continue;
159
                                }
160
                            },
161
                        };
162
163
3
                        let result = config
164
3
                            .database
165
3
                            .with_database(move |db| db.block_scale_encoded_header(&hash))
_RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s1_0B9_
Line
Count
Source
165
3
                            .with_database(move |db| db.block_scale_encoded_header(&hash))
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s1_0B9_
166
3
                            .await;
167
168
3
                        match result {
169
2
                            Ok(Some(encoded_header)) => {
170
2
                                match methods::Header::from_scale_encoded_header(
171
2
                                    &encoded_header,
172
2
                                    config.consensus_service.block_number_bytes(),
173
2
                                ) {
174
2
                                    Ok(header) => {
175
2
                                        request.respond(methods::Response::chain_getHeader(header))
176
                                    }
177
0
                                    Err(_) => {
178
0
                                        request.fail(service::ErrorResponse::InternalError);
179
0
                                    }
180
                                }
181
                            }
182
1
                            Ok(None) => {
183
1
                                request.respond_null();
184
1
                            }
185
0
                            Err(_) => {
186
0
                                request.fail(service::ErrorResponse::InternalError);
187
0
                            }
188
                        }
189
                    }
190
                    methods::MethodCall::state_getKeysPaged {
191
6
                        prefix,
192
6
                        count,
193
6
                        start_key,
194
6
                        hash,
195
6
                    } => {
196
6
                        // As an undocumented thing, a count strictly superior to 1000 isn't
197
6
                        // accepted by Substrate.
198
6
                        // See <https://github.com/paritytech/polkadot-sdk/blob/61be78c621ab2fa390cd3bfc79c8307431d0ea90/substrate/client/rpc/src/state/mod.rs#L238>.
199
6
                        if count > 1000 {
200
1
                            request.fail(service::ErrorResponse::InvalidParams);
201
1
                            continue;
202
5
                        }
203
5
204
5
                        // Turn the parameters into a format suitable for the database query.
205
5
                        let prefix_nibbles = prefix.map_or(Vec::new(), |p| {
206
5
                            trie::bytes_to_nibbles(p.0.iter().copied())
207
5
                                .map(u8::from)
208
5
                                .collect()
209
5
                        });
_RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s2_0B9_
Line
Count
Source
205
5
                        let prefix_nibbles = prefix.map_or(Vec::new(), |p| {
206
5
                            trie::bytes_to_nibbles(p.0.iter().copied())
207
5
                                .map(u8::from)
208
5
                                .collect()
209
5
                        });
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s2_0B9_
210
5
                        let mut start_key_nibbles = start_key.map_or(Vec::new(), |p| {
211
2
                            trie::bytes_to_nibbles(p.0.iter().copied())
212
2
                                .map(u8::from)
213
2
                                .collect()
214
5
                        });
_RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s3_0B9_
Line
Count
Source
210
2
                        let mut start_key_nibbles = start_key.map_or(Vec::new(), |p| {
211
2
                            trie::bytes_to_nibbles(p.0.iter().copied())
212
2
                                .map(u8::from)
213
2
                                .collect()
214
2
                        });
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s3_0B9_
215
5
216
5
                        // There's a difference of semantics between `state_getKeysPaged` and
217
5
                        // the database query we perform below in the situation where `start_key`
218
5
                        // isn't within `prefix`: the database request will return nothing while
219
5
                        // the JSON-RPC request expects the first key within `prefix`. As such,
220
5
                        // we adjust the start key if necessary.
221
5
                        // TODO: add documentation and a test in the database code regarding this behavior
222
5
                        if start_key_nibbles < prefix_nibbles {
223
1
                            start_key_nibbles = prefix_nibbles.clone();
224
4
                        }
225
226
                        // Continue in the background.
227
5
                        let result = config
228
5
                            .database
229
5
                            .with_database(
230
5
                                move |db| -> Result<_, database_thread::StorageAccessError> {
231
5
                                    let hash = match hash {
232
1
                                        Some(h) => h.0,
233
4
                                        None => db.best_block_hash()
?0
,
234
                                    };
235
236
5
                                    let mut out =
237
5
                                        Vec::with_capacity(usize::try_from(count).unwrap());
238
5
239
5
                                    let mut key_iter = start_key_nibbles;
240
241
                                    // The query is performed by repeatedly asking for the next
242
                                    // key.
243
54
                                    while out.len() < usize::try_from(count).unwrap() {
244
51
                                        let 
next_key_nibbles50
= db.block_storage_next_key(
245
51
                                            &hash,
246
51
                                            iter::empty::<iter::Empty<_>>(),
247
51
                                            key_iter.iter().copied(),
248
51
                                            prefix_nibbles.iter().copied(),
249
51
                                            false,
250
51
                                        )
?1
;
251
252
50
                                        let Some(
next_key_nibbles49
) = next_key_nibbles else {
253
1
                                            break;
254
                                        };
255
256
49
                                        out.push(methods::HexString(
257
49
                                            trie::nibbles_to_bytes_truncate(
258
49
                                                next_key_nibbles
259
49
                                                    .iter()
260
49
                                                    .copied()
261
4.64k
                                                    .map(|n| trie::Nibble::try_from(n).unwrap()),
_RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s4_00Bb_
Line
Count
Source
261
4.64k
                                                    .map(|n| trie::Nibble::try_from(n).unwrap()),
Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s4_00Bb_
262
49
                                            )
263
49
                                            .collect::<Vec<_>>(),
264
49
                                        ));
265
49
266
49
                                        // Push an extra nibble as otherwise `block_storage_next_key`
267
49
                                        // will return the same key again.
268
49
                                        key_iter = next_key_nibbles;
269
49
                                        key_iter.push(0);
270
                                    }
271
272
4
                                    Ok(out)
273
5
                                },
_RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s4_0B9_
Line
Count
Source
230
5
                                move |db| -> Result<_, database_thread::StorageAccessError> {
231
5
                                    let hash = match hash {
232
1
                                        Some(h) => h.0,
233
4
                                        None => db.best_block_hash()
?0
,
234
                                    };
235
236
5
                                    let mut out =
237
5
                                        Vec::with_capacity(usize::try_from(count).unwrap());
238
5
239
5
                                    let mut key_iter = start_key_nibbles;
240
241
                                    // The query is performed by repeatedly asking for the next
242
                                    // key.
243
54
                                    while out.len() < usize::try_from(count).unwrap() {
244
51
                                        let 
next_key_nibbles50
= db.block_storage_next_key(
245
51
                                            &hash,
246
51
                                            iter::empty::<iter::Empty<_>>(),
247
51
                                            key_iter.iter().copied(),
248
51
                                            prefix_nibbles.iter().copied(),
249
51
                                            false,
250
51
                                        )
?1
;
251
252
50
                                        let Some(
next_key_nibbles49
) = next_key_nibbles else {
253
1
                                            break;
254
                                        };
255
256
49
                                        out.push(methods::HexString(
257
49
                                            trie::nibbles_to_bytes_truncate(
258
49
                                                next_key_nibbles
259
49
                                                    .iter()
260
49
                                                    .copied()
261
49
                                                    .map(|n| trie::Nibble::try_from(n).unwrap()),
262
49
                                            )
263
49
                                            .collect::<Vec<_>>(),
264
49
                                        ));
265
49
266
49
                                        // Push an extra nibble as otherwise `block_storage_next_key`
267
49
                                        // will return the same key again.
268
49
                                        key_iter = next_key_nibbles;
269
49
                                        key_iter.push(0);
270
                                    }
271
272
4
                                    Ok(out)
273
5
                                },
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s4_0B9_
274
5
                            )
275
5
                            .await;
276
277
                        // Send back outcome.
278
1
                        match result {
279
4
                            Ok(out) => {
280
4
                                request.respond(methods::Response::state_getKeysPaged(out));
281
4
                            }
282
                            Err(database_thread::StorageAccessError::IncompleteStorage)
283
1
                            | Err(database_thread::StorageAccessError::UnknownBlock) => {
284
1
                                // Note that it is unclear how the function should behave in
285
1
                                // that situation.
286
1
                                request.fail(service::ErrorResponse::InvalidParams);
287
1
                            }
288
0
                            Err(database_thread::StorageAccessError::Corrupted(_)) => {
289
0
                                request.fail(service::ErrorResponse::InternalError);
290
0
                            }
291
                        }
292
                    }
293
1
                    methods::MethodCall::state_getMetadata { hash } => {
294
1
                        let hash = match hash {
295
1
                            Some(h) => h.0,
296
0
                            None => match config
297
0
                                .database
298
0
                                .with_database(|db| db.best_block_hash())
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s5_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s5_0B9_
299
0
                                .await
300
                            {
301
0
                                Ok(b) => b,
302
                                Err(_) => {
303
0
                                    request.fail(service::ErrorResponse::InternalError);
304
0
                                    continue;
305
                                }
306
                            },
307
                        };
308
309
1
                        let runtime = match config.runtime_caches_service.get(hash).await {
310
1
                            Ok(runtime) => (*runtime).clone(),
311
                            Err(runtime_caches_service::GetError::UnknownBlock)
312
                            | Err(runtime_caches_service::GetError::Pruned) => {
313
0
                                request.respond_null();
314
0
                                continue;
315
                            } // TODO: unclear if correct error
316
                            Err(runtime_caches_service::GetError::InvalidRuntime(_))
317
                            | Err(runtime_caches_service::GetError::NoCode)
318
                            | Err(runtime_caches_service::GetError::InvalidHeapPages)
319
                            | Err(runtime_caches_service::GetError::CorruptedDatabase) => {
320
0
                                request.fail(service::ErrorResponse::InternalError);
321
0
                                continue;
322
                            }
323
                        };
324
325
1
                        let mut call =
326
1
                            match executor::runtime_call::run(executor::runtime_call::Config {
327
1
                                virtual_machine: runtime,
328
1
                                function_to_call: "Metadata_metadata",
329
1
                                parameter: iter::empty::<&'static [u8]>(),
330
1
                                max_log_level: 0,
331
1
                                storage_proof_size_behavior: executor::runtime_call::StorageProofSizeBehavior::proof_recording_disabled(),
332
1
                                storage_main_trie_changes: Default::default(),
333
1
                                calculate_trie_changes: false,
334
1
                            }) {
335
1
                                Ok(c) => c,
336
                                Err(_) => {
337
0
                                    request.fail(service::ErrorResponse::InternalError);
338
0
                                    continue;
339
                                }
340
                            };
341
342
                        loop {
343
1
                            match call {
344
1
                                executor::runtime_call::RuntimeCall::Finished(Ok(success)) => {
345
1
                                    match methods::remove_metadata_length_prefix(success.virtual_machine.value().as_ref()) {
346
1
                                        Ok(m) => request.respond(methods::Response::state_getMetadata(methods::HexString(m.to_vec()))),
347
0
                                        Err(_) => {
348
0
                                            request.fail(service::ErrorResponse::InternalError);
349
0
                                        }
350
                                    }
351
1
                                    break;
352
                                }
353
                                executor::runtime_call::RuntimeCall::Finished(Err(_)) => {
354
0
                                    request.fail(service::ErrorResponse::InternalError);
355
0
                                    break;
356
                                }
357
0
                                executor::runtime_call::RuntimeCall::StorageGet(req) => {
358
0
                                    let parent_paths = req.child_trie().map(|child_trie| {
359
0
                                        trie::bytes_to_nibbles(
360
0
                                            b":child_storage:default:".iter().copied(),
361
0
                                        )
362
0
                                        .chain(trie::bytes_to_nibbles(
363
0
                                            child_trie.as_ref().iter().copied(),
364
0
                                        ))
365
0
                                        .map(u8::from)
366
0
                                        .collect::<Vec<_>>()
367
0
                                    });
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s6_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s6_0B9_
368
0
                                    let key =
369
0
                                        trie::bytes_to_nibbles(req.key().as_ref().iter().copied())
370
0
                                            .map(u8::from)
371
0
                                            .collect::<Vec<_>>();
372
0
                                    let value = config
373
0
                                        .database
374
0
                                        .with_database(move |db| {
375
0
                                            db.block_storage_get(
376
0
                                                &hash,
377
0
                                                parent_paths.into_iter().map(|p| p.into_iter()),
Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00Bb_
Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00Bb_
378
0
                                                key.iter().copied(),
379
0
                                            )
380
0
                                        })
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_0B9_
381
0
                                        .await;
382
0
                                    let Ok(value) = value else {
383
0
                                        request.fail(service::ErrorResponse::InternalError);
384
0
                                        break;
385
                                    };
386
0
                                    let value = value.as_ref().map(|(val, vers)| {
387
0
                                        (
388
0
                                            iter::once(&val[..]),
389
0
                                            executor::runtime_call::TrieEntryVersion::try_from(*vers)
390
0
                                                .expect("corrupted database"),
391
0
                                        )
392
0
                                    });
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s8_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s8_0B9_
393
0
394
0
                                    call = req.inject_value(value);
395
                                }
396
0
                                executor::runtime_call::RuntimeCall::ClosestDescendantMerkleValue(req) => {
397
0
                                    let parent_paths = req.child_trie().map(|child_trie| {
398
0
                                        trie::bytes_to_nibbles(
399
0
                                            b":child_storage:default:".iter().copied(),
400
0
                                        )
401
0
                                        .chain(trie::bytes_to_nibbles(
402
0
                                            child_trie.as_ref().iter().copied(),
403
0
                                        ))
404
0
                                        .map(u8::from)
405
0
                                        .collect::<Vec<_>>()
406
0
                                    });
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s9_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s9_0B9_
407
0
                                    let key_nibbles = req.key().map(u8::from).collect::<Vec<_>>();
408
409
0
                                    let merkle_value = config
410
0
                                        .database
411
0
                                        .with_database(move |db| {
412
0
                                            db.block_storage_closest_descendant_merkle_value(
413
0
                                                &hash,
414
0
                                                parent_paths.into_iter().map(|p| p.into_iter()),
Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00Bb_
Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00Bb_
415
0
                                                key_nibbles.iter().copied(),
416
0
                                            )
417
0
                                        })
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_0B9_
418
0
                                        .await;
419
420
0
                                    let Ok(merkle_value) = merkle_value else {
421
0
                                        request.fail(service::ErrorResponse::InternalError);
422
0
                                        break;
423
                                    };
424
425
0
                                    call = req
426
0
                                        .inject_merkle_value(merkle_value.as_ref().map(|v| &v[..]));
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sb_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sb_0B9_
427
                                }
428
0
                                executor::runtime_call::RuntimeCall::NextKey(req) => {
429
0
                                    let parent_paths = req.child_trie().map(|child_trie| {
430
0
                                        trie::bytes_to_nibbles(
431
0
                                            b":child_storage:default:".iter().copied(),
432
0
                                        )
433
0
                                        .chain(trie::bytes_to_nibbles(
434
0
                                            child_trie.as_ref().iter().copied(),
435
0
                                        ))
436
0
                                        .map(u8::from)
437
0
                                        .collect::<Vec<_>>()
438
0
                                    });
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sc_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sc_0B9_
439
0
                                    let key_nibbles = req
440
0
                                        .key()
441
0
                                        .map(u8::from)
442
0
                                        .chain(if req.or_equal() { None } else { Some(0u8) })
443
0
                                        .collect::<Vec<_>>();
444
0
                                    let prefix_nibbles =
445
0
                                        req.prefix().map(u8::from).collect::<Vec<_>>();
446
0
447
0
                                    let branch_nodes = req.branch_nodes();
448
0
                                    let next_key = config
449
0
                                        .database
450
0
                                        .with_database(move |db| {
451
0
                                            db.block_storage_next_key(
452
0
                                                &hash,
453
0
                                                parent_paths.into_iter().map(|p| p.into_iter()),
Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00Bb_
Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00Bb_
454
0
                                                key_nibbles.iter().copied(),
455
0
                                                prefix_nibbles.iter().copied(),
456
0
                                                branch_nodes,
457
0
                                            )
458
0
                                        })
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_0B9_
459
0
                                        .await;
460
461
0
                                    let Ok(next_key) = next_key else {
462
0
                                        request.fail(service::ErrorResponse::InternalError);
463
0
                                        break;
464
                                    };
465
466
0
                                    call = req.inject_key(next_key.map(|k| {
467
0
                                        k.into_iter().map(|b| trie::Nibble::try_from(b).unwrap())
Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0se_00Bb_
Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0se_00Bb_
468
0
                                    }));
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0se_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0se_0B9_
469
                                }
470
0
                                executor::runtime_call::RuntimeCall::OffchainStorageSet(req) => {
471
0
                                    call = req.resume();
472
0
                                }
473
0
                                executor::runtime_call::RuntimeCall::SignatureVerification(req) => {
474
0
                                    call = req.verify_and_resume();
475
0
                                }
476
                                executor::runtime_call::RuntimeCall::Offchain(_) => {
477
0
                                    request.fail(service::ErrorResponse::InternalError);
478
0
                                    break;
479
                                }
480
0
                                executor::runtime_call::RuntimeCall::LogEmit(req) => {
481
0
                                    // Logs are ignored.
482
0
                                    call = req.resume();
483
0
                                }
484
                            }
485
                        }
486
                    }
487
1
                    methods::MethodCall::state_getRuntimeVersion { at } => {
488
1
                        let at = match at {
489
1
                            Some(h) => h.0,
490
0
                            None => match config
491
0
                                .database
492
0
                                .with_database(|db| db.best_block_hash())
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sf_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sf_0B9_
493
0
                                .await
494
                            {
495
0
                                Ok(b) => b,
496
                                Err(_) => {
497
0
                                    request.fail(service::ErrorResponse::InternalError);
498
0
                                    continue;
499
                                }
500
                            },
501
                        };
502
503
1
                        match config.runtime_caches_service.get(at).await {
504
1
                            Ok(runtime) => {
505
1
                                request.respond(methods::Response::state_getRuntimeVersion(
506
1
                                    convert_runtime_version(runtime.runtime_version()),
507
1
                                ));
508
1
                            }
509
                            Err(runtime_caches_service::GetError::UnknownBlock)
510
                            | Err(runtime_caches_service::GetError::Pruned) => {
511
0
                                request.respond_null()
512
                            } // TODO: unclear if correct error
513
                            Err(runtime_caches_service::GetError::InvalidRuntime(_))
514
                            | Err(runtime_caches_service::GetError::NoCode)
515
                            | Err(runtime_caches_service::GetError::InvalidHeapPages)
516
                            | Err(runtime_caches_service::GetError::CorruptedDatabase) => {
517
0
                                request.fail(service::ErrorResponse::InternalError)
518
                            }
519
                        }
520
                    }
521
0
                    methods::MethodCall::state_queryStorageAt { keys, at } => {
522
0
                        // TODO: add a limit to the number of keys?
523
0
524
0
                        // Convert the list of keys into a format suitable for the database.
525
0
                        let keys_nibbles = keys
526
0
                            .iter()
527
0
                            .map(|key| {
528
0
                                trie::bytes_to_nibbles(key.0.iter().copied())
529
0
                                    .map(u8::from)
530
0
                                    .collect::<Vec<_>>()
531
0
                            })
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sg_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sg_0B9_
532
0
                            .collect::<Vec<_>>();
533
534
                        // The bulk of the request is performed in the database thread.
535
0
                        let result = config
536
0
                            .database
537
0
                            .with_database(move |db| {
538
0
                                let at = match at {
539
0
                                    Some(h) => h.0,
540
0
                                    None => db.best_block_hash()?,
541
                                };
542
543
0
                                let parent = db
544
0
                                    .block_parent(&at)?
545
0
                                    .ok_or(database_thread::StorageAccessError::UnknownBlock)?;
546
547
0
                                let mut out = methods::StorageChangeSet {
548
0
                                    block: methods::HashHexString(at),
549
0
                                    changes: Vec::with_capacity(keys_nibbles.len()),
550
0
                                };
551
552
0
                                for (key_nibbles, key) in
553
0
                                    keys_nibbles.into_iter().zip(keys.into_iter())
554
                                {
555
0
                                    let before = match db.block_storage_get(
556
0
                                        &parent,
557
0
                                        iter::empty::<iter::Empty<_>>(),
558
0
                                        key_nibbles.iter().copied(),
559
0
                                    ) {
560
0
                                        Ok(v) => v,
561
                                        Err(database_thread::StorageAccessError::UnknownBlock)
562
0
                                            if parent == [0; 32] =>
563
0
                                        {
564
0
                                            // In case where `at` is the genesis block, we
565
0
                                            // assume that its "parent" (which doesn't exist)
566
0
                                            // has an empty storage.
567
0
                                            None
568
                                        }
569
0
                                        Err(err) => return Err(err),
570
                                    };
571
572
0
                                    let after = db.block_storage_get(
573
0
                                        &at,
574
0
                                        iter::empty::<iter::Empty<_>>(),
575
0
                                        key_nibbles.iter().copied(),
576
0
                                    )?;
577
578
0
                                    if before != after {
579
0
                                        out.changes
580
0
                                            .push((key, after.map(|(v, _)| methods::HexString(v))));
Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sh_00Bb_
Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sh_00Bb_
581
0
                                    }
582
                                }
583
584
0
                                Ok(out)
585
0
                            })
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sh_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sh_0B9_
586
0
                            .await;
587
588
                        // Send back the response.
589
0
                        match result {
590
0
                            Ok(out) => {
591
0
                                request.respond(methods::Response::state_queryStorageAt(vec![out]));
592
0
                            }
593
                            Err(database_thread::StorageAccessError::IncompleteStorage)
594
0
                            | Err(database_thread::StorageAccessError::UnknownBlock) => {
595
0
                                // Note that it is unclear how the function should behave in
596
0
                                // that situation.
597
0
                                request.fail(service::ErrorResponse::InvalidParams);
598
0
                            }
599
0
                            Err(database_thread::StorageAccessError::Corrupted(_)) => {
600
0
                                request.fail(service::ErrorResponse::InternalError);
601
0
                            }
602
                        }
603
                    }
604
1
                    methods::MethodCall::system_chain {} => {
605
1
                        request
606
1
                            .respond(methods::Response::system_chain((&config.chain_name).into()));
607
1
                    }
608
1
                    methods::MethodCall::system_chainType {} => {
609
1
                        request.respond(methods::Response::system_chainType(
610
1
                            (&config.chain_type).into(),
611
1
                        ));
612
1
                    }
613
1
                    methods::MethodCall::system_health {} => {
614
1
                        let (is_syncing, peers) = future::zip(
615
1
                            config.consensus_service.is_major_syncing_hint(),
616
1
                            config.network_service.0.num_peers(config.network_service.1),
617
1
                        )
618
1
                        .await;
619
620
1
                        request.respond(methods::Response::system_health(methods::SystemHealth {
621
1
                            is_syncing,
622
1
                            peers: u64::try_from(peers).unwrap_or(u64::MAX),
623
1
                            should_have_peers: config.chain_is_live,
624
1
                        }));
625
                    }
626
1
                    methods::MethodCall::system_localPeerId {} => {
627
1
                        let peer_id = config.network_service.0.local_peer_id().to_base58();
628
1
                        request.respond(methods::Response::system_localPeerId(peer_id.into()));
629
1
                    }
630
1
                    methods::MethodCall::system_name {} => {
631
1
                        request.respond(methods::Response::system_version(
632
1
                            env!("CARGO_PKG_NAME").into(),
633
1
                        ));
634
1
                    }
635
1
                    methods::MethodCall::system_properties {} => {
636
1
                        request.respond(methods::Response::system_properties(
637
1
                            serde_json::from_str(&config.chain_properties_json).unwrap(),
638
1
                        ));
639
1
                    }
640
1
                    methods::MethodCall::system_version {} => {
641
1
                        request.respond(methods::Response::system_version(
642
1
                            env!("CARGO_PKG_VERSION").into(),
643
1
                        ));
644
1
                    }
645
646
0
                    _ => request.fail(service::ErrorResponse::ServerError(
647
0
                        -32000,
648
0
                        "Not implemented in smoldot yet",
649
0
                    )),
650
                },
651
0
                Some(Message::SubscriptionStart(request)) => match request.request() {
652
0
                    methods::MethodCall::chain_subscribeAllHeads {} => {
653
0
                        let block_number_bytes = config.consensus_service.block_number_bytes();
654
0
                        let mut blocks_to_report = legacy_api_subscriptions::SubscribeAllHeads::new(
655
0
                            config.consensus_service.clone(),
656
0
                        );
657
0
658
0
                        (config.tasks_executor)(Box::pin(async move {
659
0
                            let mut subscription = request.accept();
660
0
                            let subscription_id = subscription.subscription_id().to_owned();
661
662
                            loop {
663
0
                                let scale_encoded_header =
664
0
                                    blocks_to_report.next_scale_encoded_header().await;
665
666
0
                                let json_rpc_header =
667
0
                                    match methods::Header::from_scale_encoded_header(
668
0
                                        &scale_encoded_header,
669
0
                                        block_number_bytes,
670
0
                                    ) {
671
0
                                        Ok(h) => h,
672
                                        Err(_) => {
673
                                            // TODO: consider reporting to logs
674
0
                                            continue;
675
                                        }
676
                                    };
677
678
0
                                subscription
679
0
                                    .send_notification(methods::ServerToClient::chain_allHead {
680
0
                                        subscription: (&subscription_id).into(),
681
0
                                        result: json_rpc_header.clone(),
682
0
                                    })
683
0
                                    .await
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0si_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0si_0B9_
684
                            }
685
0
                        }));
686
0
                    }
687
688
0
                    methods::MethodCall::chain_subscribeFinalizedHeads {} => {
689
0
                        let block_number_bytes = config.consensus_service.block_number_bytes();
690
0
                        let mut blocks_to_report =
691
0
                            legacy_api_subscriptions::SubscribeFinalizedHeads::new(
692
0
                                config.consensus_service.clone(),
693
0
                            );
694
0
695
0
                        (config.tasks_executor)(Box::pin(async move {
696
0
                            let mut subscription = request.accept();
697
0
                            let subscription_id = subscription.subscription_id().to_owned();
698
699
                            loop {
700
0
                                let scale_encoded_header =
701
0
                                    blocks_to_report.next_scale_encoded_header().await;
702
703
0
                                let json_rpc_header =
704
0
                                    match methods::Header::from_scale_encoded_header(
705
0
                                        &scale_encoded_header,
706
0
                                        block_number_bytes,
707
0
                                    ) {
708
0
                                        Ok(h) => h,
709
                                        Err(_) => {
710
                                            // TODO: consider reporting to logs
711
0
                                            continue;
712
                                        }
713
                                    };
714
715
0
                                subscription
716
0
                                    .send_notification(
717
0
                                        methods::ServerToClient::chain_finalizedHead {
718
0
                                            subscription: (&subscription_id).into(),
719
0
                                            result: json_rpc_header.clone(),
720
0
                                        },
721
0
                                    )
722
0
                                    .await
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sj_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sj_0B9_
723
                            }
724
0
                        }));
725
0
                    }
726
727
0
                    methods::MethodCall::chain_subscribeNewHeads {} => {
728
0
                        let block_number_bytes = config.consensus_service.block_number_bytes();
729
0
                        let mut blocks_to_report = legacy_api_subscriptions::SubscribeNewHeads::new(
730
0
                            config.consensus_service.clone(),
731
0
                        );
732
0
733
0
                        (config.tasks_executor)(Box::pin(async move {
734
0
                            let mut subscription = request.accept();
735
0
                            let subscription_id = subscription.subscription_id().to_owned();
736
737
                            loop {
738
0
                                let scale_encoded_header =
739
0
                                    blocks_to_report.next_scale_encoded_header().await;
740
741
0
                                let json_rpc_header =
742
0
                                    match methods::Header::from_scale_encoded_header(
743
0
                                        scale_encoded_header,
744
0
                                        block_number_bytes,
745
0
                                    ) {
746
0
                                        Ok(h) => h,
747
                                        Err(_) => {
748
                                            // TODO: consider reporting to logs
749
0
                                            continue;
750
                                        }
751
                                    };
752
753
0
                                subscription
754
0
                                    .send_notification(methods::ServerToClient::chain_newHead {
755
0
                                        subscription: (&subscription_id).into(),
756
0
                                        result: json_rpc_header.clone(),
757
0
                                    })
758
0
                                    .await
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sk_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sk_0B9_
759
                            }
760
0
                        }));
761
0
                    }
762
763
0
                    methods::MethodCall::state_subscribeRuntimeVersion {} => {
764
0
                        let mut runtime_versions_to_report =
765
0
                            legacy_api_subscriptions::SubscribeRuntimeVersion::new(
766
0
                                config.consensus_service.clone(),
767
0
                            );
768
0
769
0
                        (config.tasks_executor)(Box::pin(async move {
770
0
                            let mut subscription = request.accept();
771
0
                            let subscription_id = subscription.subscription_id().to_owned();
772
773
                            loop {
774
0
                                let runtime_version =
775
0
                                    runtime_versions_to_report.next_runtime_version().await;
776
777
0
                                subscription
778
0
                                    .send_notification(
779
0
                                        methods::ServerToClient::state_runtimeVersion {
780
0
                                            subscription: (&subscription_id).into(),
781
0
                                            result: Some(convert_runtime_version(runtime_version)),
782
0
                                        },
783
0
                                    )
784
0
                                    .await
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sl_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sl_0B9_
785
                            }
786
0
                        }));
787
0
                    }
788
789
0
                    methods::MethodCall::state_subscribeStorage { list } => {
790
0
                        let mut notifications_to_report =
791
0
                            legacy_api_subscriptions::SubscribeStorage::new(
792
0
                                config.consensus_service.clone(),
793
0
                                config.database.clone(),
794
0
                                list.into_iter().map(|item| item.0).collect(),
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sm_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sm_0B9_
795
0
                            );
796
0
797
0
                        (config.tasks_executor)(Box::pin(async move {
798
0
                            let mut subscription = request.accept();
799
0
                            let subscription_id = subscription.subscription_id().to_owned();
800
801
                            loop {
802
0
                                let (block_hash, storage_changes) =
803
0
                                    notifications_to_report.next_storage_update().await;
804
805
0
                                subscription
806
0
                                    .send_notification(methods::ServerToClient::state_storage {
807
0
                                        subscription: (&subscription_id).into(),
808
0
                                        result: methods::StorageChangeSet {
809
0
                                            block: methods::HashHexString(block_hash),
810
0
                                            changes: storage_changes
811
0
                                                .map(|(key, value)| {
812
0
                                                    (
813
0
                                                        methods::HexString(key),
814
0
                                                        value.map(methods::HexString),
815
0
                                                    )
816
0
                                                })
Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sn_00Bb_
Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sn_00Bb_
817
0
                                                .collect(),
818
0
                                        },
819
0
                                    })
820
0
                                    .await
Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sn_0B9_
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sn_0B9_
821
                            }
822
0
                        }));
823
0
                    }
824
825
0
                    _ => request.fail(service::ErrorResponse::ServerError(
826
0
                        -32000,
827
0
                        "Not implemented in smoldot yet",
828
0
                    )),
829
                },
830
672
                None => return,
831
672
            }
832
672
        }
833
672
    }));
_RNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0B7_
Line
Count
Source
89
672
    tasks_executor(Box::pin(async move {
90
672
        let mut receiver = pin::pin!(config.receiver);
91
        loop {
92
1.28k
            match 
receiver.next()695
.await {
93
23
                Some(Message::Request(request)) => match request.request() {
94
0
                    methods::MethodCall::rpc_methods {} => {
95
0
                        request.respond(methods::Response::rpc_methods(methods::RpcMethods {
96
0
                            methods: methods::MethodCall::method_names()
97
0
                                .map(|n| n.into())
98
0
                                .collect(),
99
0
                        }));
100
0
                    }
101
102
1
                    methods::MethodCall::chainSpec_v1_chainName {} => {
103
1
                        request.respond(methods::Response::chainSpec_v1_chainName(
104
1
                            (&config.chain_name).into(),
105
1
                        ));
106
1
                    }
107
1
                    methods::MethodCall::chainSpec_v1_genesisHash {} => {
108
1
                        request.respond(methods::Response::chainSpec_v1_genesisHash(
109
1
                            methods::HashHexString(config.genesis_block_hash),
110
1
                        ));
111
1
                    }
112
1
                    methods::MethodCall::chainSpec_v1_properties {} => {
113
1
                        request.respond(methods::Response::chainSpec_v1_properties(
114
1
                            serde_json::from_str(&config.chain_properties_json).unwrap(),
115
1
                        ));
116
1
                    }
117
118
                    methods::MethodCall::chain_getBlockHash { height: Some(0) } => {
119
                        // In the case where the database was populated through a warp sync, it
120
                        // might not store block 0 in it. However, the hash of block 0 is
121
                        // particularly important for JSON-RPC clients, and as such we make sure
122
                        // to always respond successfully to block 0 requests, even if it isn't
123
                        // in the database.
124
1
                        request.respond(methods::Response::chain_getBlockHash(
125
1
                            methods::HashHexString(config.genesis_block_hash),
126
1
                        ))
127
                    }
128
1
                    methods::MethodCall::chain_getBlockHash { height } => {
129
1
                        let outcome = config
130
1
                            .database
131
1
                            .with_database(move |database| match height {
132
                                Some(height) => database.best_block_hash_by_number(height),
133
                                None => database.best_block_hash().map(Some),
134
1
                            })
135
1
                            .await;
136
1
                        match outcome {
137
0
                            Ok(Some(hash)) => request.respond(
138
0
                                methods::Response::chain_getBlockHash(methods::HashHexString(hash)),
139
0
                            ),
140
1
                            Ok(None) => request.respond_null(),
141
0
                            Err(error) => {
142
0
                                config.log_callback.log(LogLevel::Warn, format!("json-rpc; request=chain_getBlockHash; height={:?}; database_error={}", height, error));
143
0
                                request.fail(parse::ErrorResponse::InternalError)
144
                            }
145
                        }
146
                    }
147
3
                    methods::MethodCall::chain_getHeader { hash } => {
148
3
                        let hash = match hash {
149
2
                            Some(h) => h.0,
150
1
                            None => match config
151
1
                                .database
152
1
                                .with_database(|db| db.best_block_hash())
153
1
                                .await
154
                            {
155
1
                                Ok(b) => b,
156
                                Err(_) => {
157
0
                                    request.fail(service::ErrorResponse::InternalError);
158
0
                                    continue;
159
                                }
160
                            },
161
                        };
162
163
3
                        let result = config
164
3
                            .database
165
3
                            .with_database(move |db| db.block_scale_encoded_header(&hash))
166
3
                            .await;
167
168
3
                        match result {
169
2
                            Ok(Some(encoded_header)) => {
170
2
                                match methods::Header::from_scale_encoded_header(
171
2
                                    &encoded_header,
172
2
                                    config.consensus_service.block_number_bytes(),
173
2
                                ) {
174
2
                                    Ok(header) => {
175
2
                                        request.respond(methods::Response::chain_getHeader(header))
176
                                    }
177
0
                                    Err(_) => {
178
0
                                        request.fail(service::ErrorResponse::InternalError);
179
0
                                    }
180
                                }
181
                            }
182
1
                            Ok(None) => {
183
1
                                request.respond_null();
184
1
                            }
185
0
                            Err(_) => {
186
0
                                request.fail(service::ErrorResponse::InternalError);
187
0
                            }
188
                        }
189
                    }
190
                    methods::MethodCall::state_getKeysPaged {
191
6
                        prefix,
192
6
                        count,
193
6
                        start_key,
194
6
                        hash,
195
6
                    } => {
196
6
                        // As an undocumented thing, a count strictly superior to 1000 isn't
197
6
                        // accepted by Substrate.
198
6
                        // See <https://github.com/paritytech/polkadot-sdk/blob/61be78c621ab2fa390cd3bfc79c8307431d0ea90/substrate/client/rpc/src/state/mod.rs#L238>.
199
6
                        if count > 1000 {
200
1
                            request.fail(service::ErrorResponse::InvalidParams);
201
1
                            continue;
202
5
                        }
203
5
204
5
                        // Turn the parameters into a format suitable for the database query.
205
5
                        let prefix_nibbles = prefix.map_or(Vec::new(), |p| {
206
                            trie::bytes_to_nibbles(p.0.iter().copied())
207
                                .map(u8::from)
208
                                .collect()
209
5
                        });
210
5
                        let mut start_key_nibbles = start_key.map_or(Vec::new(), |p| {
211
                            trie::bytes_to_nibbles(p.0.iter().copied())
212
                                .map(u8::from)
213
                                .collect()
214
5
                        });
215
5
216
5
                        // There's a difference of semantics between `state_getKeysPaged` and
217
5
                        // the database query we perform below in the situation where `start_key`
218
5
                        // isn't within `prefix`: the database request will return nothing while
219
5
                        // the JSON-RPC request expects the first key within `prefix`. As such,
220
5
                        // we adjust the start key if necessary.
221
5
                        // TODO: add documentation and a test in the database code regarding this behavior
222
5
                        if start_key_nibbles < prefix_nibbles {
223
1
                            start_key_nibbles = prefix_nibbles.clone();
224
4
                        }
225
226
                        // Continue in the background.
227
5
                        let result = config
228
5
                            .database
229
5
                            .with_database(
230
5
                                move |db| -> Result<_, database_thread::StorageAccessError> {
231
                                    let hash = match hash {
232
                                        Some(h) => h.0,
233
                                        None => db.best_block_hash()?,
234
                                    };
235
236
                                    let mut out =
237
                                        Vec::with_capacity(usize::try_from(count).unwrap());
238
239
                                    let mut key_iter = start_key_nibbles;
240
241
                                    // The query is performed by repeatedly asking for the next
242
                                    // key.
243
                                    while out.len() < usize::try_from(count).unwrap() {
244
                                        let next_key_nibbles = db.block_storage_next_key(
245
                                            &hash,
246
                                            iter::empty::<iter::Empty<_>>(),
247
                                            key_iter.iter().copied(),
248
                                            prefix_nibbles.iter().copied(),
249
                                            false,
250
                                        )?;
251
252
                                        let Some(next_key_nibbles) = next_key_nibbles else {
253
                                            break;
254
                                        };
255
256
                                        out.push(methods::HexString(
257
                                            trie::nibbles_to_bytes_truncate(
258
                                                next_key_nibbles
259
                                                    .iter()
260
                                                    .copied()
261
                                                    .map(|n| trie::Nibble::try_from(n).unwrap()),
262
                                            )
263
                                            .collect::<Vec<_>>(),
264
                                        ));
265
266
                                        // Push an extra nibble as otherwise `block_storage_next_key`
267
                                        // will return the same key again.
268
                                        key_iter = next_key_nibbles;
269
                                        key_iter.push(0);
270
                                    }
271
272
                                    Ok(out)
273
5
                                },
274
5
                            )
275
5
                            .await;
276
277
                        // Send back outcome.
278
1
                        match result {
279
4
                            Ok(out) => {
280
4
                                request.respond(methods::Response::state_getKeysPaged(out));
281
4
                            }
282
                            Err(database_thread::StorageAccessError::IncompleteStorage)
283
1
                            | Err(database_thread::StorageAccessError::UnknownBlock) => {
284
1
                                // Note that it is unclear how the function should behave in
285
1
                                // that situation.
286
1
                                request.fail(service::ErrorResponse::InvalidParams);
287
1
                            }
288
0
                            Err(database_thread::StorageAccessError::Corrupted(_)) => {
289
0
                                request.fail(service::ErrorResponse::InternalError);
290
0
                            }
291
                        }
292
                    }
293
1
                    methods::MethodCall::state_getMetadata { hash } => {
294
1
                        let hash = match hash {
295
1
                            Some(h) => h.0,
296
0
                            None => match config
297
0
                                .database
298
0
                                .with_database(|db| db.best_block_hash())
299
0
                                .await
300
                            {
301
0
                                Ok(b) => b,
302
                                Err(_) => {
303
0
                                    request.fail(service::ErrorResponse::InternalError);
304
0
                                    continue;
305
                                }
306
                            },
307
                        };
308
309
1
                        let runtime = match config.runtime_caches_service.get(hash).await {
310
1
                            Ok(runtime) => (*runtime).clone(),
311
                            Err(runtime_caches_service::GetError::UnknownBlock)
312
                            | Err(runtime_caches_service::GetError::Pruned) => {
313
0
                                request.respond_null();
314
0
                                continue;
315
                            } // TODO: unclear if correct error
316
                            Err(runtime_caches_service::GetError::InvalidRuntime(_))
317
                            | Err(runtime_caches_service::GetError::NoCode)
318
                            | Err(runtime_caches_service::GetError::InvalidHeapPages)
319
                            | Err(runtime_caches_service::GetError::CorruptedDatabase) => {
320
0
                                request.fail(service::ErrorResponse::InternalError);
321
0
                                continue;
322
                            }
323
                        };
324
325
1
                        let mut call =
326
1
                            match executor::runtime_call::run(executor::runtime_call::Config {
327
1
                                virtual_machine: runtime,
328
1
                                function_to_call: "Metadata_metadata",
329
1
                                parameter: iter::empty::<&'static [u8]>(),
330
1
                                max_log_level: 0,
331
1
                                storage_proof_size_behavior: executor::runtime_call::StorageProofSizeBehavior::proof_recording_disabled(),
332
1
                                storage_main_trie_changes: Default::default(),
333
1
                                calculate_trie_changes: false,
334
1
                            }) {
335
1
                                Ok(c) => c,
336
                                Err(_) => {
337
0
                                    request.fail(service::ErrorResponse::InternalError);
338
0
                                    continue;
339
                                }
340
                            };
341
342
                        loop {
343
1
                            match call {
344
1
                                executor::runtime_call::RuntimeCall::Finished(Ok(success)) => {
345
1
                                    match methods::remove_metadata_length_prefix(success.virtual_machine.value().as_ref()) {
346
1
                                        Ok(m) => request.respond(methods::Response::state_getMetadata(methods::HexString(m.to_vec()))),
347
0
                                        Err(_) => {
348
0
                                            request.fail(service::ErrorResponse::InternalError);
349
0
                                        }
350
                                    }
351
1
                                    break;
352
                                }
353
                                executor::runtime_call::RuntimeCall::Finished(Err(_)) => {
354
0
                                    request.fail(service::ErrorResponse::InternalError);
355
0
                                    break;
356
                                }
357
0
                                executor::runtime_call::RuntimeCall::StorageGet(req) => {
358
0
                                    let parent_paths = req.child_trie().map(|child_trie| {
359
                                        trie::bytes_to_nibbles(
360
                                            b":child_storage:default:".iter().copied(),
361
                                        )
362
                                        .chain(trie::bytes_to_nibbles(
363
                                            child_trie.as_ref().iter().copied(),
364
                                        ))
365
                                        .map(u8::from)
366
                                        .collect::<Vec<_>>()
367
0
                                    });
368
0
                                    let key =
369
0
                                        trie::bytes_to_nibbles(req.key().as_ref().iter().copied())
370
0
                                            .map(u8::from)
371
0
                                            .collect::<Vec<_>>();
372
0
                                    let value = config
373
0
                                        .database
374
0
                                        .with_database(move |db| {
375
                                            db.block_storage_get(
376
                                                &hash,
377
                                                parent_paths.into_iter().map(|p| p.into_iter()),
378
                                                key.iter().copied(),
379
                                            )
380
0
                                        })
381
0
                                        .await;
382
0
                                    let Ok(value) = value else {
383
0
                                        request.fail(service::ErrorResponse::InternalError);
384
0
                                        break;
385
                                    };
386
0
                                    let value = value.as_ref().map(|(val, vers)| {
387
                                        (
388
                                            iter::once(&val[..]),
389
                                            executor::runtime_call::TrieEntryVersion::try_from(*vers)
390
                                                .expect("corrupted database"),
391
                                        )
392
0
                                    });
393
0
394
0
                                    call = req.inject_value(value);
395
                                }
396
0
                                executor::runtime_call::RuntimeCall::ClosestDescendantMerkleValue(req) => {
397
0
                                    let parent_paths = req.child_trie().map(|child_trie| {
398
                                        trie::bytes_to_nibbles(
399
                                            b":child_storage:default:".iter().copied(),
400
                                        )
401
                                        .chain(trie::bytes_to_nibbles(
402
                                            child_trie.as_ref().iter().copied(),
403
                                        ))
404
                                        .map(u8::from)
405
                                        .collect::<Vec<_>>()
406
0
                                    });
407
0
                                    let key_nibbles = req.key().map(u8::from).collect::<Vec<_>>();
408
409
0
                                    let merkle_value = config
410
0
                                        .database
411
0
                                        .with_database(move |db| {
412
                                            db.block_storage_closest_descendant_merkle_value(
413
                                                &hash,
414
                                                parent_paths.into_iter().map(|p| p.into_iter()),
415
                                                key_nibbles.iter().copied(),
416
                                            )
417
0
                                        })
418
0
                                        .await;
419
420
0
                                    let Ok(merkle_value) = merkle_value else {
421
0
                                        request.fail(service::ErrorResponse::InternalError);
422
0
                                        break;
423
                                    };
424
425
0
                                    call = req
426
0
                                        .inject_merkle_value(merkle_value.as_ref().map(|v| &v[..]));
427
                                }
428
0
                                executor::runtime_call::RuntimeCall::NextKey(req) => {
429
0
                                    let parent_paths = req.child_trie().map(|child_trie| {
430
                                        trie::bytes_to_nibbles(
431
                                            b":child_storage:default:".iter().copied(),
432
                                        )
433
                                        .chain(trie::bytes_to_nibbles(
434
                                            child_trie.as_ref().iter().copied(),
435
                                        ))
436
                                        .map(u8::from)
437
                                        .collect::<Vec<_>>()
438
0
                                    });
439
0
                                    let key_nibbles = req
440
0
                                        .key()
441
0
                                        .map(u8::from)
442
0
                                        .chain(if req.or_equal() { None } else { Some(0u8) })
443
0
                                        .collect::<Vec<_>>();
444
0
                                    let prefix_nibbles =
445
0
                                        req.prefix().map(u8::from).collect::<Vec<_>>();
446
0
447
0
                                    let branch_nodes = req.branch_nodes();
448
0
                                    let next_key = config
449
0
                                        .database
450
0
                                        .with_database(move |db| {
451
                                            db.block_storage_next_key(
452
                                                &hash,
453
                                                parent_paths.into_iter().map(|p| p.into_iter()),
454
                                                key_nibbles.iter().copied(),
455
                                                prefix_nibbles.iter().copied(),
456
                                                branch_nodes,
457
                                            )
458
0
                                        })
459
0
                                        .await;
460
461
0
                                    let Ok(next_key) = next_key else {
462
0
                                        request.fail(service::ErrorResponse::InternalError);
463
0
                                        break;
464
                                    };
465
466
0
                                    call = req.inject_key(next_key.map(|k| {
467
                                        k.into_iter().map(|b| trie::Nibble::try_from(b).unwrap())
468
0
                                    }));
469
                                }
470
0
                                executor::runtime_call::RuntimeCall::OffchainStorageSet(req) => {
471
0
                                    call = req.resume();
472
0
                                }
473
0
                                executor::runtime_call::RuntimeCall::SignatureVerification(req) => {
474
0
                                    call = req.verify_and_resume();
475
0
                                }
476
                                executor::runtime_call::RuntimeCall::Offchain(_) => {
477
0
                                    request.fail(service::ErrorResponse::InternalError);
478
0
                                    break;
479
                                }
480
0
                                executor::runtime_call::RuntimeCall::LogEmit(req) => {
481
0
                                    // Logs are ignored.
482
0
                                    call = req.resume();
483
0
                                }
484
                            }
485
                        }
486
                    }
487
1
                    methods::MethodCall::state_getRuntimeVersion { at } => {
488
1
                        let at = match at {
489
1
                            Some(h) => h.0,
490
0
                            None => match config
491
0
                                .database
492
0
                                .with_database(|db| db.best_block_hash())
493
0
                                .await
494
                            {
495
0
                                Ok(b) => b,
496
                                Err(_) => {
497
0
                                    request.fail(service::ErrorResponse::InternalError);
498
0
                                    continue;
499
                                }
500
                            },
501
                        };
502
503
1
                        match config.runtime_caches_service.get(at).await {
504
1
                            Ok(runtime) => {
505
1
                                request.respond(methods::Response::state_getRuntimeVersion(
506
1
                                    convert_runtime_version(runtime.runtime_version()),
507
1
                                ));
508
1
                            }
509
                            Err(runtime_caches_service::GetError::UnknownBlock)
510
                            | Err(runtime_caches_service::GetError::Pruned) => {
511
0
                                request.respond_null()
512
                            } // TODO: unclear if correct error
513
                            Err(runtime_caches_service::GetError::InvalidRuntime(_))
514
                            | Err(runtime_caches_service::GetError::NoCode)
515
                            | Err(runtime_caches_service::GetError::InvalidHeapPages)
516
                            | Err(runtime_caches_service::GetError::CorruptedDatabase) => {
517
0
                                request.fail(service::ErrorResponse::InternalError)
518
                            }
519
                        }
520
                    }
521
0
                    methods::MethodCall::state_queryStorageAt { keys, at } => {
522
0
                        // TODO: add a limit to the number of keys?
523
0
524
0
                        // Convert the list of keys into a format suitable for the database.
525
0
                        let keys_nibbles = keys
526
0
                            .iter()
527
0
                            .map(|key| {
528
                                trie::bytes_to_nibbles(key.0.iter().copied())
529
                                    .map(u8::from)
530
                                    .collect::<Vec<_>>()
531
0
                            })
532
0
                            .collect::<Vec<_>>();
533
534
                        // The bulk of the request is performed in the database thread.
535
0
                        let result = config
536
0
                            .database
537
0
                            .with_database(move |db| {
538
                                let at = match at {
539
                                    Some(h) => h.0,
540
                                    None => db.best_block_hash()?,
541
                                };
542
543
                                let parent = db
544
                                    .block_parent(&at)?
545
                                    .ok_or(database_thread::StorageAccessError::UnknownBlock)?;
546
547
                                let mut out = methods::StorageChangeSet {
548
                                    block: methods::HashHexString(at),
549
                                    changes: Vec::with_capacity(keys_nibbles.len()),
550
                                };
551
552
                                for (key_nibbles, key) in
553
                                    keys_nibbles.into_iter().zip(keys.into_iter())
554
                                {
555
                                    let before = match db.block_storage_get(
556
                                        &parent,
557
                                        iter::empty::<iter::Empty<_>>(),
558
                                        key_nibbles.iter().copied(),
559
                                    ) {
560
                                        Ok(v) => v,
561
                                        Err(database_thread::StorageAccessError::UnknownBlock)
562
                                            if parent == [0; 32] =>
563
                                        {
564
                                            // In case where `at` is the genesis block, we
565
                                            // assume that its "parent" (which doesn't exist)
566
                                            // has an empty storage.
567
                                            None
568
                                        }
569
                                        Err(err) => return Err(err),
570
                                    };
571
572
                                    let after = db.block_storage_get(
573
                                        &at,
574
                                        iter::empty::<iter::Empty<_>>(),
575
                                        key_nibbles.iter().copied(),
576
                                    )?;
577
578
                                    if before != after {
579
                                        out.changes
580
                                            .push((key, after.map(|(v, _)| methods::HexString(v))));
581
                                    }
582
                                }
583
584
                                Ok(out)
585
0
                            })
586
0
                            .await;
587
588
                        // Send back the response.
589
0
                        match result {
590
0
                            Ok(out) => {
591
0
                                request.respond(methods::Response::state_queryStorageAt(vec![out]));
592
0
                            }
593
                            Err(database_thread::StorageAccessError::IncompleteStorage)
594
0
                            | Err(database_thread::StorageAccessError::UnknownBlock) => {
595
0
                                // Note that it is unclear how the function should behave in
596
0
                                // that situation.
597
0
                                request.fail(service::ErrorResponse::InvalidParams);
598
0
                            }
599
0
                            Err(database_thread::StorageAccessError::Corrupted(_)) => {
600
0
                                request.fail(service::ErrorResponse::InternalError);
601
0
                            }
602
                        }
603
                    }
604
1
                    methods::MethodCall::system_chain {} => {
605
1
                        request
606
1
                            .respond(methods::Response::system_chain((&config.chain_name).into()));
607
1
                    }
608
1
                    methods::MethodCall::system_chainType {} => {
609
1
                        request.respond(methods::Response::system_chainType(
610
1
                            (&config.chain_type).into(),
611
1
                        ));
612
1
                    }
613
1
                    methods::MethodCall::system_health {} => {
614
1
                        let (is_syncing, peers) = future::zip(
615
1
                            config.consensus_service.is_major_syncing_hint(),
616
1
                            config.network_service.0.num_peers(config.network_service.1),
617
1
                        )
618
1
                        .await;
619
620
1
                        request.respond(methods::Response::system_health(methods::SystemHealth {
621
1
                            is_syncing,
622
1
                            peers: u64::try_from(peers).unwrap_or(u64::MAX),
623
1
                            should_have_peers: config.chain_is_live,
624
1
                        }));
625
                    }
626
1
                    methods::MethodCall::system_localPeerId {} => {
627
1
                        let peer_id = config.network_service.0.local_peer_id().to_base58();
628
1
                        request.respond(methods::Response::system_localPeerId(peer_id.into()));
629
1
                    }
630
1
                    methods::MethodCall::system_name {} => {
631
1
                        request.respond(methods::Response::system_version(
632
1
                            env!("CARGO_PKG_NAME").into(),
633
1
                        ));
634
1
                    }
635
1
                    methods::MethodCall::system_properties {} => {
636
1
                        request.respond(methods::Response::system_properties(
637
1
                            serde_json::from_str(&config.chain_properties_json).unwrap(),
638
1
                        ));
639
1
                    }
640
1
                    methods::MethodCall::system_version {} => {
641
1
                        request.respond(methods::Response::system_version(
642
1
                            env!("CARGO_PKG_VERSION").into(),
643
1
                        ));
644
1
                    }
645
646
0
                    _ => request.fail(service::ErrorResponse::ServerError(
647
0
                        -32000,
648
0
                        "Not implemented in smoldot yet",
649
0
                    )),
650
                },
651
0
                Some(Message::SubscriptionStart(request)) => match request.request() {
652
0
                    methods::MethodCall::chain_subscribeAllHeads {} => {
653
0
                        let block_number_bytes = config.consensus_service.block_number_bytes();
654
0
                        let mut blocks_to_report = legacy_api_subscriptions::SubscribeAllHeads::new(
655
0
                            config.consensus_service.clone(),
656
0
                        );
657
0
658
0
                        (config.tasks_executor)(Box::pin(async move {
659
                            let mut subscription = request.accept();
660
                            let subscription_id = subscription.subscription_id().to_owned();
661
662
                            loop {
663
                                let scale_encoded_header =
664
                                    blocks_to_report.next_scale_encoded_header().await;
665
666
                                let json_rpc_header =
667
                                    match methods::Header::from_scale_encoded_header(
668
                                        &scale_encoded_header,
669
                                        block_number_bytes,
670
                                    ) {
671
                                        Ok(h) => h,
672
                                        Err(_) => {
673
                                            // TODO: consider reporting to logs
674
                                            continue;
675
                                        }
676
                                    };
677
678
                                subscription
679
                                    .send_notification(methods::ServerToClient::chain_allHead {
680
                                        subscription: (&subscription_id).into(),
681
                                        result: json_rpc_header.clone(),
682
                                    })
683
                                    .await
684
                            }
685
0
                        }));
686
0
                    }
687
688
0
                    methods::MethodCall::chain_subscribeFinalizedHeads {} => {
689
0
                        let block_number_bytes = config.consensus_service.block_number_bytes();
690
0
                        let mut blocks_to_report =
691
0
                            legacy_api_subscriptions::SubscribeFinalizedHeads::new(
692
0
                                config.consensus_service.clone(),
693
0
                            );
694
0
695
0
                        (config.tasks_executor)(Box::pin(async move {
696
                            let mut subscription = request.accept();
697
                            let subscription_id = subscription.subscription_id().to_owned();
698
699
                            loop {
700
                                let scale_encoded_header =
701
                                    blocks_to_report.next_scale_encoded_header().await;
702
703
                                let json_rpc_header =
704
                                    match methods::Header::from_scale_encoded_header(
705
                                        &scale_encoded_header,
706
                                        block_number_bytes,
707
                                    ) {
708
                                        Ok(h) => h,
709
                                        Err(_) => {
710
                                            // TODO: consider reporting to logs
711
                                            continue;
712
                                        }
713
                                    };
714
715
                                subscription
716
                                    .send_notification(
717
                                        methods::ServerToClient::chain_finalizedHead {
718
                                            subscription: (&subscription_id).into(),
719
                                            result: json_rpc_header.clone(),
720
                                        },
721
                                    )
722
                                    .await
723
                            }
724
0
                        }));
725
0
                    }
726
727
0
                    methods::MethodCall::chain_subscribeNewHeads {} => {
728
0
                        let block_number_bytes = config.consensus_service.block_number_bytes();
729
0
                        let mut blocks_to_report = legacy_api_subscriptions::SubscribeNewHeads::new(
730
0
                            config.consensus_service.clone(),
731
0
                        );
732
0
733
0
                        (config.tasks_executor)(Box::pin(async move {
734
                            let mut subscription = request.accept();
735
                            let subscription_id = subscription.subscription_id().to_owned();
736
737
                            loop {
738
                                let scale_encoded_header =
739
                                    blocks_to_report.next_scale_encoded_header().await;
740
741
                                let json_rpc_header =
742
                                    match methods::Header::from_scale_encoded_header(
743
                                        scale_encoded_header,
744
                                        block_number_bytes,
745
                                    ) {
746
                                        Ok(h) => h,
747
                                        Err(_) => {
748
                                            // TODO: consider reporting to logs
749
                                            continue;
750
                                        }
751
                                    };
752
753
                                subscription
754
                                    .send_notification(methods::ServerToClient::chain_newHead {
755
                                        subscription: (&subscription_id).into(),
756
                                        result: json_rpc_header.clone(),
757
                                    })
758
                                    .await
759
                            }
760
0
                        }));
761
0
                    }
762
763
0
                    methods::MethodCall::state_subscribeRuntimeVersion {} => {
764
0
                        let mut runtime_versions_to_report =
765
0
                            legacy_api_subscriptions::SubscribeRuntimeVersion::new(
766
0
                                config.consensus_service.clone(),
767
0
                            );
768
0
769
0
                        (config.tasks_executor)(Box::pin(async move {
770
                            let mut subscription = request.accept();
771
                            let subscription_id = subscription.subscription_id().to_owned();
772
773
                            loop {
774
                                let runtime_version =
775
                                    runtime_versions_to_report.next_runtime_version().await;
776
777
                                subscription
778
                                    .send_notification(
779
                                        methods::ServerToClient::state_runtimeVersion {
780
                                            subscription: (&subscription_id).into(),
781
                                            result: Some(convert_runtime_version(runtime_version)),
782
                                        },
783
                                    )
784
                                    .await
785
                            }
786
0
                        }));
787
0
                    }
788
789
0
                    methods::MethodCall::state_subscribeStorage { list } => {
790
0
                        let mut notifications_to_report =
791
0
                            legacy_api_subscriptions::SubscribeStorage::new(
792
0
                                config.consensus_service.clone(),
793
0
                                config.database.clone(),
794
0
                                list.into_iter().map(|item| item.0).collect(),
795
0
                            );
796
0
797
0
                        (config.tasks_executor)(Box::pin(async move {
798
                            let mut subscription = request.accept();
799
                            let subscription_id = subscription.subscription_id().to_owned();
800
801
                            loop {
802
                                let (block_hash, storage_changes) =
803
                                    notifications_to_report.next_storage_update().await;
804
805
                                subscription
806
                                    .send_notification(methods::ServerToClient::state_storage {
807
                                        subscription: (&subscription_id).into(),
808
                                        result: methods::StorageChangeSet {
809
                                            block: methods::HashHexString(block_hash),
810
                                            changes: storage_changes
811
                                                .map(|(key, value)| {
812
                                                    (
813
                                                        methods::HexString(key),
814
                                                        value.map(methods::HexString),
815
                                                    )
816
                                                })
817
                                                .collect(),
818
                                        },
819
                                    })
820
                                    .await
821
                            }
822
0
                        }));
823
0
                    }
824
825
0
                    _ => request.fail(service::ErrorResponse::ServerError(
826
0
                        -32000,
827
0
                        "Not implemented in smoldot yet",
828
0
                    )),
829
                },
830
672
                None => return,
831
672
            }
832
672
        }
833
672
    }));
Unexecuted instantiation: _RNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0B7_
834
672
}
_RNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler
Line
Count
Source
87
672
pub fn spawn_requests_handler(config: Config) {
88
672
    let tasks_executor = config.tasks_executor.clone();
89
672
    tasks_executor(Box::pin(async move {
90
        let mut receiver = pin::pin!(config.receiver);
91
        loop {
92
            match receiver.next().await {
93
                Some(Message::Request(request)) => match request.request() {
94
                    methods::MethodCall::rpc_methods {} => {
95
                        request.respond(methods::Response::rpc_methods(methods::RpcMethods {
96
                            methods: methods::MethodCall::method_names()
97
                                .map(|n| n.into())
98
                                .collect(),
99
                        }));
100
                    }
101
102
                    methods::MethodCall::chainSpec_v1_chainName {} => {
103
                        request.respond(methods::Response::chainSpec_v1_chainName(
104
                            (&config.chain_name).into(),
105
                        ));
106
                    }
107
                    methods::MethodCall::chainSpec_v1_genesisHash {} => {
108
                        request.respond(methods::Response::chainSpec_v1_genesisHash(
109
                            methods::HashHexString(config.genesis_block_hash),
110
                        ));
111
                    }
112
                    methods::MethodCall::chainSpec_v1_properties {} => {
113
                        request.respond(methods::Response::chainSpec_v1_properties(
114
                            serde_json::from_str(&config.chain_properties_json).unwrap(),
115
                        ));
116
                    }
117
118
                    methods::MethodCall::chain_getBlockHash { height: Some(0) } => {
119
                        // In the case where the database was populated through a warp sync, it
120
                        // might not store block 0 in it. However, the hash of block 0 is
121
                        // particularly important for JSON-RPC clients, and as such we make sure
122
                        // to always respond successfully to block 0 requests, even if it isn't
123
                        // in the database.
124
                        request.respond(methods::Response::chain_getBlockHash(
125
                            methods::HashHexString(config.genesis_block_hash),
126
                        ))
127
                    }
128
                    methods::MethodCall::chain_getBlockHash { height } => {
129
                        let outcome = config
130
                            .database
131
                            .with_database(move |database| match height {
132
                                Some(height) => database.best_block_hash_by_number(height),
133
                                None => database.best_block_hash().map(Some),
134
                            })
135
                            .await;
136
                        match outcome {
137
                            Ok(Some(hash)) => request.respond(
138
                                methods::Response::chain_getBlockHash(methods::HashHexString(hash)),
139
                            ),
140
                            Ok(None) => request.respond_null(),
141
                            Err(error) => {
142
                                config.log_callback.log(LogLevel::Warn, format!("json-rpc; request=chain_getBlockHash; height={:?}; database_error={}", height, error));
143
                                request.fail(parse::ErrorResponse::InternalError)
144
                            }
145
                        }
146
                    }
147
                    methods::MethodCall::chain_getHeader { hash } => {
148
                        let hash = match hash {
149
                            Some(h) => h.0,
150
                            None => match config
151
                                .database
152
                                .with_database(|db| db.best_block_hash())
153
                                .await
154
                            {
155
                                Ok(b) => b,
156
                                Err(_) => {
157
                                    request.fail(service::ErrorResponse::InternalError);
158
                                    continue;
159
                                }
160
                            },
161
                        };
162
163
                        let result = config
164
                            .database
165
                            .with_database(move |db| db.block_scale_encoded_header(&hash))
166
                            .await;
167
168
                        match result {
169
                            Ok(Some(encoded_header)) => {
170
                                match methods::Header::from_scale_encoded_header(
171
                                    &encoded_header,
172
                                    config.consensus_service.block_number_bytes(),
173
                                ) {
174
                                    Ok(header) => {
175
                                        request.respond(methods::Response::chain_getHeader(header))
176
                                    }
177
                                    Err(_) => {
178
                                        request.fail(service::ErrorResponse::InternalError);
179
                                    }
180
                                }
181
                            }
182
                            Ok(None) => {
183
                                request.respond_null();
184
                            }
185
                            Err(_) => {
186
                                request.fail(service::ErrorResponse::InternalError);
187
                            }
188
                        }
189
                    }
190
                    methods::MethodCall::state_getKeysPaged {
191
                        prefix,
192
                        count,
193
                        start_key,
194
                        hash,
195
                    } => {
196
                        // As an undocumented thing, a count strictly superior to 1000 isn't
197
                        // accepted by Substrate.
198
                        // See <https://github.com/paritytech/polkadot-sdk/blob/61be78c621ab2fa390cd3bfc79c8307431d0ea90/substrate/client/rpc/src/state/mod.rs#L238>.
199
                        if count > 1000 {
200
                            request.fail(service::ErrorResponse::InvalidParams);
201
                            continue;
202
                        }
203
204
                        // Turn the parameters into a format suitable for the database query.
205
                        let prefix_nibbles = prefix.map_or(Vec::new(), |p| {
206
                            trie::bytes_to_nibbles(p.0.iter().copied())
207
                                .map(u8::from)
208
                                .collect()
209
                        });
210
                        let mut start_key_nibbles = start_key.map_or(Vec::new(), |p| {
211
                            trie::bytes_to_nibbles(p.0.iter().copied())
212
                                .map(u8::from)
213
                                .collect()
214
                        });
215
216
                        // There's a difference of semantics between `state_getKeysPaged` and
217
                        // the database query we perform below in the situation where `start_key`
218
                        // isn't within `prefix`: the database request will return nothing while
219
                        // the JSON-RPC request expects the first key within `prefix`. As such,
220
                        // we adjust the start key if necessary.
221
                        // TODO: add documentation and a test in the database code regarding this behavior
222
                        if start_key_nibbles < prefix_nibbles {
223
                            start_key_nibbles = prefix_nibbles.clone();
224
                        }
225
226
                        // Continue in the background.
227
                        let result = config
228
                            .database
229
                            .with_database(
230
                                move |db| -> Result<_, database_thread::StorageAccessError> {
231
                                    let hash = match hash {
232
                                        Some(h) => h.0,
233
                                        None => db.best_block_hash()?,
234
                                    };
235
236
                                    let mut out =
237
                                        Vec::with_capacity(usize::try_from(count).unwrap());
238
239
                                    let mut key_iter = start_key_nibbles;
240
241
                                    // The query is performed by repeatedly asking for the next
242
                                    // key.
243
                                    while out.len() < usize::try_from(count).unwrap() {
244
                                        let next_key_nibbles = db.block_storage_next_key(
245
                                            &hash,
246
                                            iter::empty::<iter::Empty<_>>(),
247
                                            key_iter.iter().copied(),
248
                                            prefix_nibbles.iter().copied(),
249
                                            false,
250
                                        )?;
251
252
                                        let Some(next_key_nibbles) = next_key_nibbles else {
253
                                            break;
254
                                        };
255
256
                                        out.push(methods::HexString(
257
                                            trie::nibbles_to_bytes_truncate(
258
                                                next_key_nibbles
259
                                                    .iter()
260
                                                    .copied()
261
                                                    .map(|n| trie::Nibble::try_from(n).unwrap()),
262
                                            )
263
                                            .collect::<Vec<_>>(),
264
                                        ));
265
266
                                        // Push an extra nibble as otherwise `block_storage_next_key`
267
                                        // will return the same key again.
268
                                        key_iter = next_key_nibbles;
269
                                        key_iter.push(0);
270
                                    }
271
272
                                    Ok(out)
273
                                },
274
                            )
275
                            .await;
276
277
                        // Send back outcome.
278
                        match result {
279
                            Ok(out) => {
280
                                request.respond(methods::Response::state_getKeysPaged(out));
281
                            }
282
                            Err(database_thread::StorageAccessError::IncompleteStorage)
283
                            | Err(database_thread::StorageAccessError::UnknownBlock) => {
284
                                // Note that it is unclear how the function should behave in
285
                                // that situation.
286
                                request.fail(service::ErrorResponse::InvalidParams);
287
                            }
288
                            Err(database_thread::StorageAccessError::Corrupted(_)) => {
289
                                request.fail(service::ErrorResponse::InternalError);
290
                            }
291
                        }
292
                    }
293
                    methods::MethodCall::state_getMetadata { hash } => {
294
                        let hash = match hash {
295
                            Some(h) => h.0,
296
                            None => match config
297
                                .database
298
                                .with_database(|db| db.best_block_hash())
299
                                .await
300
                            {
301
                                Ok(b) => b,
302
                                Err(_) => {
303
                                    request.fail(service::ErrorResponse::InternalError);
304
                                    continue;
305
                                }
306
                            },
307
                        };
308
309
                        let runtime = match config.runtime_caches_service.get(hash).await {
310
                            Ok(runtime) => (*runtime).clone(),
311
                            Err(runtime_caches_service::GetError::UnknownBlock)
312
                            | Err(runtime_caches_service::GetError::Pruned) => {
313
                                request.respond_null();
314
                                continue;
315
                            } // TODO: unclear if correct error
316
                            Err(runtime_caches_service::GetError::InvalidRuntime(_))
317
                            | Err(runtime_caches_service::GetError::NoCode)
318
                            | Err(runtime_caches_service::GetError::InvalidHeapPages)
319
                            | Err(runtime_caches_service::GetError::CorruptedDatabase) => {
320
                                request.fail(service::ErrorResponse::InternalError);
321
                                continue;
322
                            }
323
                        };
324
325
                        let mut call =
326
                            match executor::runtime_call::run(executor::runtime_call::Config {
327
                                virtual_machine: runtime,
328
                                function_to_call: "Metadata_metadata",
329
                                parameter: iter::empty::<&'static [u8]>(),
330
                                max_log_level: 0,
331
                                storage_proof_size_behavior: executor::runtime_call::StorageProofSizeBehavior::proof_recording_disabled(),
332
                                storage_main_trie_changes: Default::default(),
333
                                calculate_trie_changes: false,
334
                            }) {
335
                                Ok(c) => c,
336
                                Err(_) => {
337
                                    request.fail(service::ErrorResponse::InternalError);
338
                                    continue;
339
                                }
340
                            };
341
342
                        loop {
343
                            match call {
344
                                executor::runtime_call::RuntimeCall::Finished(Ok(success)) => {
345
                                    match methods::remove_metadata_length_prefix(success.virtual_machine.value().as_ref()) {
346
                                        Ok(m) => request.respond(methods::Response::state_getMetadata(methods::HexString(m.to_vec()))),
347
                                        Err(_) => {
348
                                            request.fail(service::ErrorResponse::InternalError);
349
                                        }
350
                                    }
351
                                    break;
352
                                }
353
                                executor::runtime_call::RuntimeCall::Finished(Err(_)) => {
354
                                    request.fail(service::ErrorResponse::InternalError);
355
                                    break;
356
                                }
357
                                executor::runtime_call::RuntimeCall::StorageGet(req) => {
358
                                    let parent_paths = req.child_trie().map(|child_trie| {
359
                                        trie::bytes_to_nibbles(
360
                                            b":child_storage:default:".iter().copied(),
361
                                        )
362
                                        .chain(trie::bytes_to_nibbles(
363
                                            child_trie.as_ref().iter().copied(),
364
                                        ))
365
                                        .map(u8::from)
366
                                        .collect::<Vec<_>>()
367
                                    });
368
                                    let key =
369
                                        trie::bytes_to_nibbles(req.key().as_ref().iter().copied())
370
                                            .map(u8::from)
371
                                            .collect::<Vec<_>>();
372
                                    let value = config
373
                                        .database
374
                                        .with_database(move |db| {
375
                                            db.block_storage_get(
376
                                                &hash,
377
                                                parent_paths.into_iter().map(|p| p.into_iter()),
378
                                                key.iter().copied(),
379
                                            )
380
                                        })
381
                                        .await;
382
                                    let Ok(value) = value else {
383
                                        request.fail(service::ErrorResponse::InternalError);
384
                                        break;
385
                                    };
386
                                    let value = value.as_ref().map(|(val, vers)| {
387
                                        (
388
                                            iter::once(&val[..]),
389
                                            executor::runtime_call::TrieEntryVersion::try_from(*vers)
390
                                                .expect("corrupted database"),
391
                                        )
392
                                    });
393
394
                                    call = req.inject_value(value);
395
                                }
396
                                executor::runtime_call::RuntimeCall::ClosestDescendantMerkleValue(req) => {
397
                                    let parent_paths = req.child_trie().map(|child_trie| {
398
                                        trie::bytes_to_nibbles(
399
                                            b":child_storage:default:".iter().copied(),
400
                                        )
401
                                        .chain(trie::bytes_to_nibbles(
402
                                            child_trie.as_ref().iter().copied(),
403
                                        ))
404
                                        .map(u8::from)
405
                                        .collect::<Vec<_>>()
406
                                    });
407
                                    let key_nibbles = req.key().map(u8::from).collect::<Vec<_>>();
408
409
                                    let merkle_value = config
410
                                        .database
411
                                        .with_database(move |db| {
412
                                            db.block_storage_closest_descendant_merkle_value(
413
                                                &hash,
414
                                                parent_paths.into_iter().map(|p| p.into_iter()),
415
                                                key_nibbles.iter().copied(),
416
                                            )
417
                                        })
418
                                        .await;
419
420
                                    let Ok(merkle_value) = merkle_value else {
421
                                        request.fail(service::ErrorResponse::InternalError);
422
                                        break;
423
                                    };
424
425
                                    call = req
426
                                        .inject_merkle_value(merkle_value.as_ref().map(|v| &v[..]));
427
                                }
428
                                executor::runtime_call::RuntimeCall::NextKey(req) => {
429
                                    let parent_paths = req.child_trie().map(|child_trie| {
430
                                        trie::bytes_to_nibbles(
431
                                            b":child_storage:default:".iter().copied(),
432
                                        )
433
                                        .chain(trie::bytes_to_nibbles(
434
                                            child_trie.as_ref().iter().copied(),
435
                                        ))
436
                                        .map(u8::from)
437
                                        .collect::<Vec<_>>()
438
                                    });
439
                                    let key_nibbles = req
440
                                        .key()
441
                                        .map(u8::from)
442
                                        .chain(if req.or_equal() { None } else { Some(0u8) })
443
                                        .collect::<Vec<_>>();
444
                                    let prefix_nibbles =
445
                                        req.prefix().map(u8::from).collect::<Vec<_>>();
446
447
                                    let branch_nodes = req.branch_nodes();
448
                                    let next_key = config
449
                                        .database
450
                                        .with_database(move |db| {
451
                                            db.block_storage_next_key(
452
                                                &hash,
453
                                                parent_paths.into_iter().map(|p| p.into_iter()),
454
                                                key_nibbles.iter().copied(),
455
                                                prefix_nibbles.iter().copied(),
456
                                                branch_nodes,
457
                                            )
458
                                        })
459
                                        .await;
460
461
                                    let Ok(next_key) = next_key else {
462
                                        request.fail(service::ErrorResponse::InternalError);
463
                                        break;
464
                                    };
465
466
                                    call = req.inject_key(next_key.map(|k| {
467
                                        k.into_iter().map(|b| trie::Nibble::try_from(b).unwrap())
468
                                    }));
469
                                }
470
                                executor::runtime_call::RuntimeCall::OffchainStorageSet(req) => {
471
                                    call = req.resume();
472
                                }
473
                                executor::runtime_call::RuntimeCall::SignatureVerification(req) => {
474
                                    call = req.verify_and_resume();
475
                                }
476
                                executor::runtime_call::RuntimeCall::Offchain(_) => {
477
                                    request.fail(service::ErrorResponse::InternalError);
478
                                    break;
479
                                }
480
                                executor::runtime_call::RuntimeCall::LogEmit(req) => {
481
                                    // Logs are ignored.
482
                                    call = req.resume();
483
                                }
484
                            }
485
                        }
486
                    }
487
                    methods::MethodCall::state_getRuntimeVersion { at } => {
488
                        let at = match at {
489
                            Some(h) => h.0,
490
                            None => match config
491
                                .database
492
                                .with_database(|db| db.best_block_hash())
493
                                .await
494
                            {
495
                                Ok(b) => b,
496
                                Err(_) => {
497
                                    request.fail(service::ErrorResponse::InternalError);
498
                                    continue;
499
                                }
500
                            },
501
                        };
502
503
                        match config.runtime_caches_service.get(at).await {
504
                            Ok(runtime) => {
505
                                request.respond(methods::Response::state_getRuntimeVersion(
506
                                    convert_runtime_version(runtime.runtime_version()),
507
                                ));
508
                            }
509
                            Err(runtime_caches_service::GetError::UnknownBlock)
510
                            | Err(runtime_caches_service::GetError::Pruned) => {
511
                                request.respond_null()
512
                            } // TODO: unclear if correct error
513
                            Err(runtime_caches_service::GetError::InvalidRuntime(_))
514
                            | Err(runtime_caches_service::GetError::NoCode)
515
                            | Err(runtime_caches_service::GetError::InvalidHeapPages)
516
                            | Err(runtime_caches_service::GetError::CorruptedDatabase) => {
517
                                request.fail(service::ErrorResponse::InternalError)
518
                            }
519
                        }
520
                    }
521
                    methods::MethodCall::state_queryStorageAt { keys, at } => {
522
                        // TODO: add a limit to the number of keys?
523
524
                        // Convert the list of keys into a format suitable for the database.
525
                        let keys_nibbles = keys
526
                            .iter()
527
                            .map(|key| {
528
                                trie::bytes_to_nibbles(key.0.iter().copied())
529
                                    .map(u8::from)
530
                                    .collect::<Vec<_>>()
531
                            })
532
                            .collect::<Vec<_>>();
533
534
                        // The bulk of the request is performed in the database thread.
535
                        let result = config
536
                            .database
537
                            .with_database(move |db| {
538
                                let at = match at {
539
                                    Some(h) => h.0,
540
                                    None => db.best_block_hash()?,
541
                                };
542
543
                                let parent = db
544
                                    .block_parent(&at)?
545
                                    .ok_or(database_thread::StorageAccessError::UnknownBlock)?;
546
547
                                let mut out = methods::StorageChangeSet {
548
                                    block: methods::HashHexString(at),
549
                                    changes: Vec::with_capacity(keys_nibbles.len()),
550
                                };
551
552
                                for (key_nibbles, key) in
553
                                    keys_nibbles.into_iter().zip(keys.into_iter())
554
                                {
555
                                    let before = match db.block_storage_get(
556
                                        &parent,
557
                                        iter::empty::<iter::Empty<_>>(),
558
                                        key_nibbles.iter().copied(),
559
                                    ) {
560
                                        Ok(v) => v,
561
                                        Err(database_thread::StorageAccessError::UnknownBlock)
562
                                            if parent == [0; 32] =>
563
                                        {
564
                                            // In case where `at` is the genesis block, we
565
                                            // assume that its "parent" (which doesn't exist)
566
                                            // has an empty storage.
567
                                            None
568
                                        }
569
                                        Err(err) => return Err(err),
570
                                    };
571
572
                                    let after = db.block_storage_get(
573
                                        &at,
574
                                        iter::empty::<iter::Empty<_>>(),
575
                                        key_nibbles.iter().copied(),
576
                                    )?;
577
578
                                    if before != after {
579
                                        out.changes
580
                                            .push((key, after.map(|(v, _)| methods::HexString(v))));
581
                                    }
582
                                }
583
584
                                Ok(out)
585
                            })
586
                            .await;
587
588
                        // Send back the response.
589
                        match result {
590
                            Ok(out) => {
591
                                request.respond(methods::Response::state_queryStorageAt(vec![out]));
592
                            }
593
                            Err(database_thread::StorageAccessError::IncompleteStorage)
594
                            | Err(database_thread::StorageAccessError::UnknownBlock) => {
595
                                // Note that it is unclear how the function should behave in
596
                                // that situation.
597
                                request.fail(service::ErrorResponse::InvalidParams);
598
                            }
599
                            Err(database_thread::StorageAccessError::Corrupted(_)) => {
600
                                request.fail(service::ErrorResponse::InternalError);
601
                            }
602
                        }
603
                    }
604
                    methods::MethodCall::system_chain {} => {
605
                        request
606
                            .respond(methods::Response::system_chain((&config.chain_name).into()));
607
                    }
608
                    methods::MethodCall::system_chainType {} => {
609
                        request.respond(methods::Response::system_chainType(
610
                            (&config.chain_type).into(),
611
                        ));
612
                    }
613
                    methods::MethodCall::system_health {} => {
614
                        let (is_syncing, peers) = future::zip(
615
                            config.consensus_service.is_major_syncing_hint(),
616
                            config.network_service.0.num_peers(config.network_service.1),
617
                        )
618
                        .await;
619
620
                        request.respond(methods::Response::system_health(methods::SystemHealth {
621
                            is_syncing,
622
                            peers: u64::try_from(peers).unwrap_or(u64::MAX),
623
                            should_have_peers: config.chain_is_live,
624
                        }));
625
                    }
626
                    methods::MethodCall::system_localPeerId {} => {
627
                        let peer_id = config.network_service.0.local_peer_id().to_base58();
628
                        request.respond(methods::Response::system_localPeerId(peer_id.into()));
629
                    }
630
                    methods::MethodCall::system_name {} => {
631
                        request.respond(methods::Response::system_version(
632
                            env!("CARGO_PKG_NAME").into(),
633
                        ));
634
                    }
635
                    methods::MethodCall::system_properties {} => {
636
                        request.respond(methods::Response::system_properties(
637
                            serde_json::from_str(&config.chain_properties_json).unwrap(),
638
                        ));
639
                    }
640
                    methods::MethodCall::system_version {} => {
641
                        request.respond(methods::Response::system_version(
642
                            env!("CARGO_PKG_VERSION").into(),
643
                        ));
644
                    }
645
646
                    _ => request.fail(service::ErrorResponse::ServerError(
647
                        -32000,
648
                        "Not implemented in smoldot yet",
649
                    )),
650
                },
651
                Some(Message::SubscriptionStart(request)) => match request.request() {
652
                    methods::MethodCall::chain_subscribeAllHeads {} => {
653
                        let block_number_bytes = config.consensus_service.block_number_bytes();
654
                        let mut blocks_to_report = legacy_api_subscriptions::SubscribeAllHeads::new(
655
                            config.consensus_service.clone(),
656
                        );
657
658
                        (config.tasks_executor)(Box::pin(async move {
659
                            let mut subscription = request.accept();
660
                            let subscription_id = subscription.subscription_id().to_owned();
661
662
                            loop {
663
                                let scale_encoded_header =
664
                                    blocks_to_report.next_scale_encoded_header().await;
665
666
                                let json_rpc_header =
667
                                    match methods::Header::from_scale_encoded_header(
668
                                        &scale_encoded_header,
669
                                        block_number_bytes,
670
                                    ) {
671
                                        Ok(h) => h,
672
                                        Err(_) => {
673
                                            // TODO: consider reporting to logs
674
                                            continue;
675
                                        }
676
                                    };
677
678
                                subscription
679
                                    .send_notification(methods::ServerToClient::chain_allHead {
680
                                        subscription: (&subscription_id).into(),
681
                                        result: json_rpc_header.clone(),
682
                                    })
683
                                    .await
684
                            }
685
                        }));
686
                    }
687
688
                    methods::MethodCall::chain_subscribeFinalizedHeads {} => {
689
                        let block_number_bytes = config.consensus_service.block_number_bytes();
690
                        let mut blocks_to_report =
691
                            legacy_api_subscriptions::SubscribeFinalizedHeads::new(
692
                                config.consensus_service.clone(),
693
                            );
694
695
                        (config.tasks_executor)(Box::pin(async move {
696
                            let mut subscription = request.accept();
697
                            let subscription_id = subscription.subscription_id().to_owned();
698
699
                            loop {
700
                                let scale_encoded_header =
701
                                    blocks_to_report.next_scale_encoded_header().await;
702
703
                                let json_rpc_header =
704
                                    match methods::Header::from_scale_encoded_header(
705
                                        &scale_encoded_header,
706
                                        block_number_bytes,
707
                                    ) {
708
                                        Ok(h) => h,
709
                                        Err(_) => {
710
                                            // TODO: consider reporting to logs
711
                                            continue;
712
                                        }
713
                                    };
714
715
                                subscription
716
                                    .send_notification(
717
                                        methods::ServerToClient::chain_finalizedHead {
718
                                            subscription: (&subscription_id).into(),
719
                                            result: json_rpc_header.clone(),
720
                                        },
721
                                    )
722
                                    .await
723
                            }
724
                        }));
725
                    }
726
727
                    methods::MethodCall::chain_subscribeNewHeads {} => {
728
                        let block_number_bytes = config.consensus_service.block_number_bytes();
729
                        let mut blocks_to_report = legacy_api_subscriptions::SubscribeNewHeads::new(
730
                            config.consensus_service.clone(),
731
                        );
732
733
                        (config.tasks_executor)(Box::pin(async move {
734
                            let mut subscription = request.accept();
735
                            let subscription_id = subscription.subscription_id().to_owned();
736
737
                            loop {
738
                                let scale_encoded_header =
739
                                    blocks_to_report.next_scale_encoded_header().await;
740
741
                                let json_rpc_header =
742
                                    match methods::Header::from_scale_encoded_header(
743
                                        scale_encoded_header,
744
                                        block_number_bytes,
745
                                    ) {
746
                                        Ok(h) => h,
747
                                        Err(_) => {
748
                                            // TODO: consider reporting to logs
749
                                            continue;
750
                                        }
751
                                    };
752
753
                                subscription
754
                                    .send_notification(methods::ServerToClient::chain_newHead {
755
                                        subscription: (&subscription_id).into(),
756
                                        result: json_rpc_header.clone(),
757
                                    })
758
                                    .await
759
                            }
760
                        }));
761
                    }
762
763
                    methods::MethodCall::state_subscribeRuntimeVersion {} => {
764
                        let mut runtime_versions_to_report =
765
                            legacy_api_subscriptions::SubscribeRuntimeVersion::new(
766
                                config.consensus_service.clone(),
767
                            );
768
769
                        (config.tasks_executor)(Box::pin(async move {
770
                            let mut subscription = request.accept();
771
                            let subscription_id = subscription.subscription_id().to_owned();
772
773
                            loop {
774
                                let runtime_version =
775
                                    runtime_versions_to_report.next_runtime_version().await;
776
777
                                subscription
778
                                    .send_notification(
779
                                        methods::ServerToClient::state_runtimeVersion {
780
                                            subscription: (&subscription_id).into(),
781
                                            result: Some(convert_runtime_version(runtime_version)),
782
                                        },
783
                                    )
784
                                    .await
785
                            }
786
                        }));
787
                    }
788
789
                    methods::MethodCall::state_subscribeStorage { list } => {
790
                        let mut notifications_to_report =
791
                            legacy_api_subscriptions::SubscribeStorage::new(
792
                                config.consensus_service.clone(),
793
                                config.database.clone(),
794
                                list.into_iter().map(|item| item.0).collect(),
795
                            );
796
797
                        (config.tasks_executor)(Box::pin(async move {
798
                            let mut subscription = request.accept();
799
                            let subscription_id = subscription.subscription_id().to_owned();
800
801
                            loop {
802
                                let (block_hash, storage_changes) =
803
                                    notifications_to_report.next_storage_update().await;
804
805
                                subscription
806
                                    .send_notification(methods::ServerToClient::state_storage {
807
                                        subscription: (&subscription_id).into(),
808
                                        result: methods::StorageChangeSet {
809
                                            block: methods::HashHexString(block_hash),
810
                                            changes: storage_changes
811
                                                .map(|(key, value)| {
812
                                                    (
813
                                                        methods::HexString(key),
814
                                                        value.map(methods::HexString),
815
                                                    )
816
                                                })
817
                                                .collect(),
818
                                        },
819
                                    })
820
                                    .await
821
                            }
822
                        }));
823
                    }
824
825
                    _ => request.fail(service::ErrorResponse::ServerError(
826
                        -32000,
827
                        "Not implemented in smoldot yet",
828
                    )),
829
                },
830
                None => return,
831
            }
832
        }
833
672
    }));
834
672
}
Unexecuted instantiation: _RNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler
835
836
1
fn convert_runtime_version(runtime_spec: &executor::CoreVersion) -> methods::RuntimeVersion {
837
1
    let runtime_spec = runtime_spec.decode();
838
1
    methods::RuntimeVersion {
839
1
        spec_name: runtime_spec.spec_name.into(),
840
1
        impl_name: runtime_spec.impl_name.into(),
841
1
        authoring_version: u64::from(runtime_spec.authoring_version),
842
1
        spec_version: u64::from(runtime_spec.spec_version),
843
1
        impl_version: u64::from(runtime_spec.impl_version),
844
1
        transaction_version: runtime_spec.transaction_version.map(u64::from),
845
1
        state_version: runtime_spec.state_version.map(u8::from).map(u64::from),
846
1
        apis: runtime_spec
847
1
            .apis
848
10
            .map(|api| (methods::HexString(api.name_hash.to_vec()), api.version))
_RNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler23convert_runtime_version0B7_
Line
Count
Source
848
10
            .map(|api| (methods::HexString(api.name_hash.to_vec()), api.version))
Unexecuted instantiation: _RNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler23convert_runtime_version0B7_
849
1
            .collect(),
850
1
    }
851
1
}
_RNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler23convert_runtime_version
Line
Count
Source
836
1
fn convert_runtime_version(runtime_spec: &executor::CoreVersion) -> methods::RuntimeVersion {
837
1
    let runtime_spec = runtime_spec.decode();
838
1
    methods::RuntimeVersion {
839
1
        spec_name: runtime_spec.spec_name.into(),
840
1
        impl_name: runtime_spec.impl_name.into(),
841
1
        authoring_version: u64::from(runtime_spec.authoring_version),
842
1
        spec_version: u64::from(runtime_spec.spec_version),
843
1
        impl_version: u64::from(runtime_spec.impl_version),
844
1
        transaction_version: runtime_spec.transaction_version.map(u64::from),
845
1
        state_version: runtime_spec.state_version.map(u8::from).map(u64::from),
846
1
        apis: runtime_spec
847
1
            .apis
848
1
            .map(|api| (methods::HexString(api.name_hash.to_vec()), api.version))
849
1
            .collect(),
850
1
    }
851
1
}
Unexecuted instantiation: _RNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler23convert_runtime_version