kaspa_grpc_core/convert/
message.rs

1//! Conversions of protowire messages from and to rpc core counterparts.
2//!
3//! Response payloads in protowire do always contain an error field and generally a set of
4//! fields providing the requested data.
5//!
6//! Responses in rpc core are expressed as `RpcResult<XxxResponse>`, where `Xxx` is the called
7//! RPC method.
8//!
9//! The general conversion convention from protowire to rpc core is to consider the error
10//! field first and, if present, to return a matching Err(RpcError). If absent, try to
11//! convert the set of data fields into a matching XxxResponse rpc core response and, on
12//! success, return Ok(XxxResponse), otherwise return a conversion error.
13//!
14//! Conversely, the general conversion convention from rpc core to protowire, depending on
15//! a provided RpcResult is to either convert the Ok(XxxResponse) into the matching set
16//! of data fields and provide no error or provide no data fields but an error field in case
17//! of Err(RpcError).
18//!
19//! The SubmitBlockResponse is a notable exception to this general rule.
20
21use crate::protowire::{self, submit_block_response_message::RejectReason};
22use kaspa_consensus_core::network::NetworkId;
23use kaspa_core::debug;
24use kaspa_notify::subscription::Command;
25use kaspa_rpc_core::{
26    RpcContextualPeerAddress, RpcError, RpcExtraData, RpcHash, RpcIpAddress, RpcNetworkType, RpcPeerAddress, RpcResult,
27    SubmitBlockRejectReason, SubmitBlockReport,
28};
29use kaspa_utils::hex::*;
30use std::str::FromStr;
31
32macro_rules! from {
33    // Response capture
34    ($name:ident : RpcResult<&$from_type:ty>, $to_type:ty, $ctor:block) => {
35        impl From<RpcResult<&$from_type>> for $to_type {
36            fn from(item: RpcResult<&$from_type>) -> Self {
37                match item {
38                    Ok($name) => $ctor,
39                    Err(err) => {
40                        let mut message = Self::default();
41                        message.error = Some(err.into());
42                        message
43                    }
44                }
45            }
46        }
47    };
48
49    // Response without parameter capture
50    (RpcResult<&$from_type:ty>, $to_type:ty) => {
51        impl From<RpcResult<&$from_type>> for $to_type {
52            fn from(item: RpcResult<&$from_type>) -> Self {
53                Self { error: item.map_err(protowire::RpcError::from).err() }
54            }
55        }
56    };
57
58    // Request and other capture
59    ($name:ident : $from_type:ty, $to_type:ty, $body:block) => {
60        impl From<$from_type> for $to_type {
61            fn from($name: $from_type) -> Self {
62                $body
63            }
64        }
65    };
66
67    // Request and other without parameter capture
68    ($from_type:ty, $to_type:ty) => {
69        impl From<$from_type> for $to_type {
70            fn from(_: $from_type) -> Self {
71                Self {}
72            }
73        }
74    };
75}
76
77macro_rules! try_from {
78    // Response capture
79    ($name:ident : $from_type:ty, RpcResult<$to_type:ty>, $ctor:block) => {
80        impl TryFrom<$from_type> for $to_type {
81            type Error = RpcError;
82            fn try_from($name: $from_type) -> RpcResult<Self> {
83                if let Some(ref err) = $name.error {
84                    Err(err.into())
85                } else {
86                    #[allow(unreachable_code)] // TODO: remove attribute when all converters are implemented
87                    Ok($ctor)
88                }
89            }
90        }
91    };
92
93    // Response without parameter capture
94    ($from_type:ty, RpcResult<$to_type:ty>) => {
95        impl TryFrom<$from_type> for $to_type {
96            type Error = RpcError;
97            fn try_from(item: $from_type) -> RpcResult<Self> {
98                item.error.as_ref().map_or(Ok(Self {}), |x| Err(x.into()))
99            }
100        }
101    };
102
103    // Request and other capture
104    ($name:ident : $from_type:ty, $to_type:ty, $body:block) => {
105        impl TryFrom<$from_type> for $to_type {
106            type Error = RpcError;
107            fn try_from($name: $from_type) -> RpcResult<Self> {
108                #[allow(unreachable_code)] // TODO: remove attribute when all converters are implemented
109                Ok($body)
110            }
111        }
112    };
113
114    // Request and other without parameter capture
115    ($from_type:ty, $to_type:ty) => {
116        impl TryFrom<$from_type> for $to_type {
117            type Error = RpcError;
118            fn try_from(_: $from_type) -> RpcResult<Self> {
119                Ok(Self {})
120            }
121        }
122    };
123}
124
125// ----------------------------------------------------------------------------
126// rpc_core to protowire
127// ----------------------------------------------------------------------------
128
129from!(item: &kaspa_rpc_core::SubmitBlockReport, RejectReason, {
130    match item {
131        kaspa_rpc_core::SubmitBlockReport::Success => RejectReason::None,
132        kaspa_rpc_core::SubmitBlockReport::Reject(kaspa_rpc_core::SubmitBlockRejectReason::BlockInvalid) => RejectReason::BlockInvalid,
133        kaspa_rpc_core::SubmitBlockReport::Reject(kaspa_rpc_core::SubmitBlockRejectReason::IsInIBD) => RejectReason::IsInIbd,
134        // The conversion of RouteIsFull falls back to None since there exist no such variant in the original protowire version
135        // and we do not want to break backwards compatibility
136        kaspa_rpc_core::SubmitBlockReport::Reject(kaspa_rpc_core::SubmitBlockRejectReason::RouteIsFull) => RejectReason::None,
137    }
138});
139
140from!(item: &kaspa_rpc_core::SubmitBlockRequest, protowire::SubmitBlockRequestMessage, {
141    Self { block: Some((&item.block).into()), allow_non_daa_blocks: item.allow_non_daa_blocks }
142});
143// This conversion breaks the general conversion convention (see file header) since the message may
144// contain both a non default reject_reason and a matching error message. In the RouteIsFull case
145// reject_reason is None (because this reason has no variant in protowire) but a specific error
146// message is provided.
147from!(item: RpcResult<&kaspa_rpc_core::SubmitBlockResponse>, protowire::SubmitBlockResponseMessage, {
148    let error: Option<protowire::RpcError> = match item.report {
149        kaspa_rpc_core::SubmitBlockReport::Success => None,
150        kaspa_rpc_core::SubmitBlockReport::Reject(reason) => Some(RpcError::SubmitBlockError(reason).into())
151    };
152    Self { reject_reason: RejectReason::from(&item.report) as i32, error }
153});
154
155from!(item: &kaspa_rpc_core::GetBlockTemplateRequest, protowire::GetBlockTemplateRequestMessage, {
156    Self {
157        pay_address: (&item.pay_address).into(),
158        extra_data: String::from_utf8(item.extra_data.clone()).expect("extra data has to be valid UTF-8"),
159    }
160});
161from!(item: RpcResult<&kaspa_rpc_core::GetBlockTemplateResponse>, protowire::GetBlockTemplateResponseMessage, {
162    Self { block: Some((&item.block).into()), is_synced: item.is_synced, error: None }
163});
164
165from!(item: &kaspa_rpc_core::GetBlockRequest, protowire::GetBlockRequestMessage, {
166    Self { hash: item.hash.to_string(), include_transactions: item.include_transactions }
167});
168from!(item: RpcResult<&kaspa_rpc_core::GetBlockResponse>, protowire::GetBlockResponseMessage, {
169    Self { block: Some((&item.block).into()), error: None }
170});
171
172from!(item: &kaspa_rpc_core::NotifyBlockAddedRequest, protowire::NotifyBlockAddedRequestMessage, {
173    Self { command: item.command.into() }
174});
175from!(RpcResult<&kaspa_rpc_core::NotifyBlockAddedResponse>, protowire::NotifyBlockAddedResponseMessage);
176
177from!(&kaspa_rpc_core::GetInfoRequest, protowire::GetInfoRequestMessage);
178from!(item: RpcResult<&kaspa_rpc_core::GetInfoResponse>, protowire::GetInfoResponseMessage, {
179    Self {
180        p2p_id: item.p2p_id.clone(),
181        mempool_size: item.mempool_size,
182        server_version: item.server_version.clone(),
183        is_utxo_indexed: item.is_utxo_indexed,
184        is_synced: item.is_synced,
185        has_notify_command: item.has_notify_command,
186        has_message_id: item.has_message_id,
187        error: None,
188    }
189});
190
191from!(item: &kaspa_rpc_core::NotifyNewBlockTemplateRequest, protowire::NotifyNewBlockTemplateRequestMessage, {
192    Self { command: item.command.into() }
193});
194from!(RpcResult<&kaspa_rpc_core::NotifyNewBlockTemplateResponse>, protowire::NotifyNewBlockTemplateResponseMessage);
195
196// ~~~
197
198from!(&kaspa_rpc_core::GetCurrentNetworkRequest, protowire::GetCurrentNetworkRequestMessage);
199from!(item: RpcResult<&kaspa_rpc_core::GetCurrentNetworkResponse>, protowire::GetCurrentNetworkResponseMessage, {
200    Self { current_network: item.network.to_string(), error: None }
201});
202
203from!(&kaspa_rpc_core::GetPeerAddressesRequest, protowire::GetPeerAddressesRequestMessage);
204from!(item: RpcResult<&kaspa_rpc_core::GetPeerAddressesResponse>, protowire::GetPeerAddressesResponseMessage, {
205    Self {
206        addresses: item.known_addresses.iter().map(|x| x.into()).collect(),
207        banned_addresses: item.banned_addresses.iter().map(|x| x.into()).collect(),
208        error: None,
209    }
210});
211
212from!(&kaspa_rpc_core::GetSinkRequest, protowire::GetSinkRequestMessage);
213from!(item: RpcResult<&kaspa_rpc_core::GetSinkResponse>, protowire::GetSinkResponseMessage, {
214    Self { sink: item.sink.to_string(), error: None }
215});
216
217from!(item: &kaspa_rpc_core::GetMempoolEntryRequest, protowire::GetMempoolEntryRequestMessage, {
218    Self {
219        tx_id: item.transaction_id.to_string(),
220        include_orphan_pool: item.include_orphan_pool,
221        filter_transaction_pool: item.filter_transaction_pool,
222    }
223});
224from!(item: RpcResult<&kaspa_rpc_core::GetMempoolEntryResponse>, protowire::GetMempoolEntryResponseMessage, {
225    Self { entry: Some((&item.mempool_entry).into()), error: None }
226});
227
228from!(item: &kaspa_rpc_core::GetMempoolEntriesRequest, protowire::GetMempoolEntriesRequestMessage, {
229    Self { include_orphan_pool: item.include_orphan_pool, filter_transaction_pool: item.filter_transaction_pool }
230});
231from!(item: RpcResult<&kaspa_rpc_core::GetMempoolEntriesResponse>, protowire::GetMempoolEntriesResponseMessage, {
232    Self { entries: item.mempool_entries.iter().map(|x| x.into()).collect(), error: None }
233});
234
235from!(&kaspa_rpc_core::GetConnectedPeerInfoRequest, protowire::GetConnectedPeerInfoRequestMessage);
236from!(item: RpcResult<&kaspa_rpc_core::GetConnectedPeerInfoResponse>, protowire::GetConnectedPeerInfoResponseMessage, {
237    Self { infos: item.peer_info.iter().map(|x| x.into()).collect(), error: None }
238});
239
240from!(item: &kaspa_rpc_core::AddPeerRequest, protowire::AddPeerRequestMessage, {
241    Self { address: item.peer_address.to_string(), is_permanent: item.is_permanent }
242});
243from!(RpcResult<&kaspa_rpc_core::AddPeerResponse>, protowire::AddPeerResponseMessage);
244
245from!(item: &kaspa_rpc_core::SubmitTransactionRequest, protowire::SubmitTransactionRequestMessage, {
246    Self { transaction: Some((&item.transaction).into()), allow_orphan: item.allow_orphan }
247});
248from!(item: RpcResult<&kaspa_rpc_core::SubmitTransactionResponse>, protowire::SubmitTransactionResponseMessage, {
249    Self { transaction_id: item.transaction_id.to_string(), error: None }
250});
251
252from!(item: &kaspa_rpc_core::SubmitTransactionReplacementRequest, protowire::SubmitTransactionReplacementRequestMessage, {
253    Self { transaction: Some((&item.transaction).into()) }
254});
255from!(item: RpcResult<&kaspa_rpc_core::SubmitTransactionReplacementResponse>, protowire::SubmitTransactionReplacementResponseMessage, {
256    Self { transaction_id: item.transaction_id.to_string(), replaced_transaction: Some((&item.replaced_transaction).into()), error: None }
257});
258
259from!(item: &kaspa_rpc_core::GetSubnetworkRequest, protowire::GetSubnetworkRequestMessage, {
260    Self { subnetwork_id: item.subnetwork_id.to_string() }
261});
262from!(item: RpcResult<&kaspa_rpc_core::GetSubnetworkResponse>, protowire::GetSubnetworkResponseMessage, {
263    Self { gas_limit: item.gas_limit, error: None }
264});
265
266// ~~~
267
268from!(item: &kaspa_rpc_core::GetVirtualChainFromBlockRequest, protowire::GetVirtualChainFromBlockRequestMessage, {
269    Self { start_hash: item.start_hash.to_string(), include_accepted_transaction_ids: item.include_accepted_transaction_ids }
270});
271from!(item: RpcResult<&kaspa_rpc_core::GetVirtualChainFromBlockResponse>, protowire::GetVirtualChainFromBlockResponseMessage, {
272    Self {
273        removed_chain_block_hashes: item.removed_chain_block_hashes.iter().map(|x| x.to_string()).collect(),
274        added_chain_block_hashes: item.added_chain_block_hashes.iter().map(|x| x.to_string()).collect(),
275        accepted_transaction_ids: item.accepted_transaction_ids.iter().map(|x| x.into()).collect(),
276        error: None,
277    }
278});
279
280from!(item: &kaspa_rpc_core::GetBlocksRequest, protowire::GetBlocksRequestMessage, {
281    Self {
282        low_hash: item.low_hash.map_or(Default::default(), |x| x.to_string()),
283        include_blocks: item.include_blocks,
284        include_transactions: item.include_transactions,
285    }
286});
287from!(item: RpcResult<&kaspa_rpc_core::GetBlocksResponse>, protowire::GetBlocksResponseMessage, {
288    Self {
289        block_hashes: item.block_hashes.iter().map(|x| x.to_string()).collect::<Vec<_>>(),
290        blocks: item.blocks.iter().map(|x| x.into()).collect::<Vec<_>>(),
291        error: None,
292    }
293});
294
295from!(&kaspa_rpc_core::GetBlockCountRequest, protowire::GetBlockCountRequestMessage);
296from!(item: RpcResult<&kaspa_rpc_core::GetBlockCountResponse>, protowire::GetBlockCountResponseMessage, {
297    Self { block_count: item.block_count, header_count: item.header_count, error: None }
298});
299
300from!(&kaspa_rpc_core::GetBlockDagInfoRequest, protowire::GetBlockDagInfoRequestMessage);
301from!(item: RpcResult<&kaspa_rpc_core::GetBlockDagInfoResponse>, protowire::GetBlockDagInfoResponseMessage, {
302    Self {
303        network_name: item.network.to_prefixed(),
304        block_count: item.block_count,
305        header_count: item.header_count,
306        tip_hashes: item.tip_hashes.iter().map(|x| x.to_string()).collect(),
307        difficulty: item.difficulty,
308        past_median_time: item.past_median_time as i64,
309        virtual_parent_hashes: item.virtual_parent_hashes.iter().map(|x| x.to_string()).collect(),
310        pruning_point_hash: item.pruning_point_hash.to_string(),
311        virtual_daa_score: item.virtual_daa_score,
312        sink: item.sink.to_string(),
313        error: None,
314    }
315});
316
317from!(item: &kaspa_rpc_core::ResolveFinalityConflictRequest, protowire::ResolveFinalityConflictRequestMessage, {
318    Self { finality_block_hash: item.finality_block_hash.to_string() }
319});
320from!(_item: RpcResult<&kaspa_rpc_core::ResolveFinalityConflictResponse>, protowire::ResolveFinalityConflictResponseMessage, {
321    Self { error: None }
322});
323
324from!(&kaspa_rpc_core::ShutdownRequest, protowire::ShutdownRequestMessage);
325from!(RpcResult<&kaspa_rpc_core::ShutdownResponse>, protowire::ShutdownResponseMessage);
326
327from!(item: &kaspa_rpc_core::GetHeadersRequest, protowire::GetHeadersRequestMessage, {
328    Self { start_hash: item.start_hash.to_string(), limit: item.limit, is_ascending: item.is_ascending }
329});
330from!(item: RpcResult<&kaspa_rpc_core::GetHeadersResponse>, protowire::GetHeadersResponseMessage, {
331    Self { headers: item.headers.iter().map(|x| x.hash.to_string()).collect(), error: None }
332});
333
334from!(item: &kaspa_rpc_core::GetUtxosByAddressesRequest, protowire::GetUtxosByAddressesRequestMessage, {
335    Self { addresses: item.addresses.iter().map(|x| x.into()).collect() }
336});
337from!(item: RpcResult<&kaspa_rpc_core::GetUtxosByAddressesResponse>, protowire::GetUtxosByAddressesResponseMessage, {
338    debug!("GRPC, Creating GetUtxosByAddresses message with {} entries", item.entries.len());
339    Self { entries: item.entries.iter().map(|x| x.into()).collect(), error: None }
340});
341
342from!(item: &kaspa_rpc_core::GetBalanceByAddressRequest, protowire::GetBalanceByAddressRequestMessage, {
343    Self { address: (&item.address).into() }
344});
345from!(item: RpcResult<&kaspa_rpc_core::GetBalanceByAddressResponse>, protowire::GetBalanceByAddressResponseMessage, {
346    debug!("GRPC, Creating GetBalanceByAddress messages");
347    Self { balance: item.balance, error: None }
348});
349
350from!(item: &kaspa_rpc_core::GetBalancesByAddressesRequest, protowire::GetBalancesByAddressesRequestMessage, {
351    Self { addresses: item.addresses.iter().map(|x| x.into()).collect() }
352});
353from!(item: RpcResult<&kaspa_rpc_core::GetBalancesByAddressesResponse>, protowire::GetBalancesByAddressesResponseMessage, {
354    debug!("GRPC, Creating GetUtxosByAddresses message with {} entries", item.entries.len());
355    Self { entries: item.entries.iter().map(|x| x.into()).collect(), error: None }
356});
357
358from!(&kaspa_rpc_core::GetSinkBlueScoreRequest, protowire::GetSinkBlueScoreRequestMessage);
359from!(item: RpcResult<&kaspa_rpc_core::GetSinkBlueScoreResponse>, protowire::GetSinkBlueScoreResponseMessage, {
360    Self { blue_score: item.blue_score, error: None }
361});
362
363from!(item: &kaspa_rpc_core::BanRequest, protowire::BanRequestMessage, { Self { ip: item.ip.to_string() } });
364from!(_item: RpcResult<&kaspa_rpc_core::BanResponse>, protowire::BanResponseMessage, { Self { error: None } });
365
366from!(item: &kaspa_rpc_core::UnbanRequest, protowire::UnbanRequestMessage, { Self { ip: item.ip.to_string() } });
367from!(_item: RpcResult<&kaspa_rpc_core::UnbanResponse>, protowire::UnbanResponseMessage, { Self { error: None } });
368
369from!(item: &kaspa_rpc_core::EstimateNetworkHashesPerSecondRequest, protowire::EstimateNetworkHashesPerSecondRequestMessage, {
370    Self { window_size: item.window_size, start_hash: item.start_hash.map_or(Default::default(), |x| x.to_string()) }
371});
372from!(
373    item: RpcResult<&kaspa_rpc_core::EstimateNetworkHashesPerSecondResponse>,
374    protowire::EstimateNetworkHashesPerSecondResponseMessage,
375    { Self { network_hashes_per_second: item.network_hashes_per_second, error: None } }
376);
377
378from!(item: &kaspa_rpc_core::GetMempoolEntriesByAddressesRequest, protowire::GetMempoolEntriesByAddressesRequestMessage, {
379    Self {
380        addresses: item.addresses.iter().map(|x| x.into()).collect(),
381        include_orphan_pool: item.include_orphan_pool,
382        filter_transaction_pool: item.filter_transaction_pool,
383    }
384});
385from!(
386    item: RpcResult<&kaspa_rpc_core::GetMempoolEntriesByAddressesResponse>,
387    protowire::GetMempoolEntriesByAddressesResponseMessage,
388    { Self { entries: item.entries.iter().map(|x| x.into()).collect(), error: None } }
389);
390
391from!(&kaspa_rpc_core::GetCoinSupplyRequest, protowire::GetCoinSupplyRequestMessage);
392from!(item: RpcResult<&kaspa_rpc_core::GetCoinSupplyResponse>, protowire::GetCoinSupplyResponseMessage, {
393    Self { max_sompi: item.max_sompi, circulating_sompi: item.circulating_sompi, error: None }
394});
395
396from!(item: &kaspa_rpc_core::GetDaaScoreTimestampEstimateRequest, protowire::GetDaaScoreTimestampEstimateRequestMessage, {
397    Self {
398        daa_scores: item.daa_scores.clone()
399    }
400});
401from!(item: RpcResult<&kaspa_rpc_core::GetDaaScoreTimestampEstimateResponse>, protowire::GetDaaScoreTimestampEstimateResponseMessage, {
402    Self { timestamps: item.timestamps.clone(), error: None }
403});
404
405// Fee estimate API
406
407from!(&kaspa_rpc_core::GetFeeEstimateRequest, protowire::GetFeeEstimateRequestMessage);
408from!(item: RpcResult<&kaspa_rpc_core::GetFeeEstimateResponse>, protowire::GetFeeEstimateResponseMessage, {
409    Self { estimate: Some((&item.estimate).into()), error: None }
410});
411from!(item: &kaspa_rpc_core::GetFeeEstimateExperimentalRequest, protowire::GetFeeEstimateExperimentalRequestMessage, {
412    Self {
413        verbose: item.verbose
414    }
415});
416from!(item: RpcResult<&kaspa_rpc_core::GetFeeEstimateExperimentalResponse>, protowire::GetFeeEstimateExperimentalResponseMessage, {
417    Self {
418        estimate: Some((&item.estimate).into()),
419        verbose: item.verbose.as_ref().map(|x| x.into()),
420        error: None
421    }
422});
423
424from!(item: &kaspa_rpc_core::GetCurrentBlockColorRequest, protowire::GetCurrentBlockColorRequestMessage, {
425    Self {
426        hash: item.hash.to_string()
427    }
428});
429from!(item: RpcResult<&kaspa_rpc_core::GetCurrentBlockColorResponse>, protowire::GetCurrentBlockColorResponseMessage, {
430    Self { blue: item.blue, error: None }
431});
432
433from!(&kaspa_rpc_core::PingRequest, protowire::PingRequestMessage);
434from!(RpcResult<&kaspa_rpc_core::PingResponse>, protowire::PingResponseMessage);
435
436from!(item: &kaspa_rpc_core::GetMetricsRequest, protowire::GetMetricsRequestMessage, {
437    Self {
438        process_metrics: item.process_metrics,
439        connection_metrics: item.connection_metrics,
440        bandwidth_metrics: item.bandwidth_metrics,
441        consensus_metrics: item.consensus_metrics,
442        storage_metrics: item.storage_metrics,
443        custom_metrics: item.custom_metrics,
444    }
445});
446from!(item: RpcResult<&kaspa_rpc_core::GetMetricsResponse>, protowire::GetMetricsResponseMessage, {
447    Self {
448        server_time: item.server_time,
449        process_metrics: item.process_metrics.as_ref().map(|x| x.into()),
450        connection_metrics: item.connection_metrics.as_ref().map(|x| x.into()),
451        bandwidth_metrics: item.bandwidth_metrics.as_ref().map(|x| x.into()),
452        consensus_metrics: item.consensus_metrics.as_ref().map(|x| x.into()),
453        storage_metrics: item.storage_metrics.as_ref().map(|x| x.into()),
454        // TODO
455        // custom_metrics : None,
456        error: None,
457    }
458});
459
460from!(item: &kaspa_rpc_core::GetConnectionsRequest, protowire::GetConnectionsRequestMessage, {
461    Self {
462        include_profile_data : item.include_profile_data,
463    }
464});
465from!(item: RpcResult<&kaspa_rpc_core::GetConnectionsResponse>, protowire::GetConnectionsResponseMessage, {
466    Self {
467        clients: item.clients,
468        peers: item.peers as u32,
469        profile_data: item.profile_data.as_ref().map(|x| x.into()),
470        error: None,
471    }
472});
473
474from!(&kaspa_rpc_core::GetSystemInfoRequest, protowire::GetSystemInfoRequestMessage);
475from!(item: RpcResult<&kaspa_rpc_core::GetSystemInfoResponse>, protowire::GetSystemInfoResponseMessage, {
476    Self {
477        version : item.version.clone(),
478        system_id : item.system_id.as_ref().map(|system_id|system_id.to_hex()).unwrap_or_default(),
479        git_hash : item.git_hash.as_ref().map(|git_hash|git_hash.to_hex()).unwrap_or_default(),
480        total_memory : item.total_memory,
481        core_num : item.cpu_physical_cores as u32,
482        fd_limit : item.fd_limit,
483        proxy_socket_limit_per_cpu_core : item.proxy_socket_limit_per_cpu_core.unwrap_or_default(),
484        error: None,
485    }
486});
487
488from!(&kaspa_rpc_core::GetServerInfoRequest, protowire::GetServerInfoRequestMessage);
489from!(item: RpcResult<&kaspa_rpc_core::GetServerInfoResponse>, protowire::GetServerInfoResponseMessage, {
490    Self {
491        rpc_api_version: item.rpc_api_version as u32,
492        rpc_api_revision: item.rpc_api_revision as u32,
493        server_version: item.server_version.clone(),
494        network_id: item.network_id.to_string(),
495        has_utxo_index: item.has_utxo_index,
496        is_synced: item.is_synced,
497        virtual_daa_score: item.virtual_daa_score,
498        error: None,
499    }
500});
501
502from!(&kaspa_rpc_core::GetSyncStatusRequest, protowire::GetSyncStatusRequestMessage);
503from!(item: RpcResult<&kaspa_rpc_core::GetSyncStatusResponse>, protowire::GetSyncStatusResponseMessage, {
504    Self {
505        is_synced: item.is_synced,
506        error: None,
507    }
508});
509
510from!(item: &kaspa_rpc_core::NotifyUtxosChangedRequest, protowire::NotifyUtxosChangedRequestMessage, {
511    Self { addresses: item.addresses.iter().map(|x| x.into()).collect(), command: item.command.into() }
512});
513from!(item: &kaspa_rpc_core::NotifyUtxosChangedRequest, protowire::StopNotifyingUtxosChangedRequestMessage, {
514    Self { addresses: item.addresses.iter().map(|x| x.into()).collect() }
515});
516from!(RpcResult<&kaspa_rpc_core::NotifyUtxosChangedResponse>, protowire::NotifyUtxosChangedResponseMessage);
517from!(RpcResult<&kaspa_rpc_core::NotifyUtxosChangedResponse>, protowire::StopNotifyingUtxosChangedResponseMessage);
518
519from!(item: &kaspa_rpc_core::NotifyPruningPointUtxoSetOverrideRequest, protowire::NotifyPruningPointUtxoSetOverrideRequestMessage, {
520    Self { command: item.command.into() }
521});
522from!(&kaspa_rpc_core::NotifyPruningPointUtxoSetOverrideRequest, protowire::StopNotifyingPruningPointUtxoSetOverrideRequestMessage);
523from!(
524    RpcResult<&kaspa_rpc_core::NotifyPruningPointUtxoSetOverrideResponse>,
525    protowire::NotifyPruningPointUtxoSetOverrideResponseMessage
526);
527from!(
528    RpcResult<&kaspa_rpc_core::NotifyPruningPointUtxoSetOverrideResponse>,
529    protowire::StopNotifyingPruningPointUtxoSetOverrideResponseMessage
530);
531
532from!(item: &kaspa_rpc_core::NotifyFinalityConflictRequest, protowire::NotifyFinalityConflictRequestMessage, {
533    Self { command: item.command.into() }
534});
535from!(RpcResult<&kaspa_rpc_core::NotifyFinalityConflictResponse>, protowire::NotifyFinalityConflictResponseMessage);
536
537from!(item: &kaspa_rpc_core::NotifyVirtualDaaScoreChangedRequest, protowire::NotifyVirtualDaaScoreChangedRequestMessage, {
538    Self { command: item.command.into() }
539});
540from!(RpcResult<&kaspa_rpc_core::NotifyVirtualDaaScoreChangedResponse>, protowire::NotifyVirtualDaaScoreChangedResponseMessage);
541
542from!(item: &kaspa_rpc_core::NotifyVirtualChainChangedRequest, protowire::NotifyVirtualChainChangedRequestMessage, {
543    Self { include_accepted_transaction_ids: item.include_accepted_transaction_ids, command: item.command.into() }
544});
545from!(RpcResult<&kaspa_rpc_core::NotifyVirtualChainChangedResponse>, protowire::NotifyVirtualChainChangedResponseMessage);
546
547from!(item: &kaspa_rpc_core::NotifySinkBlueScoreChangedRequest, protowire::NotifySinkBlueScoreChangedRequestMessage, {
548    Self { command: item.command.into() }
549});
550from!(RpcResult<&kaspa_rpc_core::NotifySinkBlueScoreChangedResponse>, protowire::NotifySinkBlueScoreChangedResponseMessage);
551
552// ----------------------------------------------------------------------------
553// protowire to rpc_core
554// ----------------------------------------------------------------------------
555
556from!(item: RejectReason, kaspa_rpc_core::SubmitBlockReport, {
557    match item {
558        RejectReason::None => kaspa_rpc_core::SubmitBlockReport::Success,
559        RejectReason::BlockInvalid => kaspa_rpc_core::SubmitBlockReport::Reject(kaspa_rpc_core::SubmitBlockRejectReason::BlockInvalid),
560        RejectReason::IsInIbd => kaspa_rpc_core::SubmitBlockReport::Reject(kaspa_rpc_core::SubmitBlockRejectReason::IsInIBD),
561    }
562});
563
564try_from!(item: &protowire::SubmitBlockRequestMessage, kaspa_rpc_core::SubmitBlockRequest, {
565    Self {
566        block: item
567            .block
568            .as_ref()
569            .ok_or_else(|| RpcError::MissingRpcFieldError("SubmitBlockRequestMessage".to_string(), "block".to_string()))?
570            .try_into()?,
571        allow_non_daa_blocks: item.allow_non_daa_blocks,
572    }
573});
574impl TryFrom<&protowire::SubmitBlockResponseMessage> for kaspa_rpc_core::SubmitBlockResponse {
575    type Error = RpcError;
576    // This conversion breaks the general conversion convention (see file header) since the message may
577    // contain both a non-None reject_reason and a matching error message. Things get even challenging
578    // in the RouteIsFull case where reject_reason is None (because this reason has no variant in protowire)
579    // but a specific error message is provided.
580    fn try_from(item: &protowire::SubmitBlockResponseMessage) -> RpcResult<Self> {
581        let report: SubmitBlockReport =
582            RejectReason::try_from(item.reject_reason).map_err(|_| RpcError::PrimitiveToEnumConversionError)?.into();
583        if let Some(ref err) = item.error {
584            match report {
585                SubmitBlockReport::Success => {
586                    if err.message == RpcError::SubmitBlockError(SubmitBlockRejectReason::RouteIsFull).to_string() {
587                        Ok(Self { report: SubmitBlockReport::Reject(SubmitBlockRejectReason::RouteIsFull) })
588                    } else {
589                        Err(err.into())
590                    }
591                }
592                SubmitBlockReport::Reject(_) => Ok(Self { report }),
593            }
594        } else {
595            Ok(Self { report })
596        }
597    }
598}
599
600try_from!(item: &protowire::GetBlockTemplateRequestMessage, kaspa_rpc_core::GetBlockTemplateRequest, {
601    Self { pay_address: item.pay_address.clone().try_into()?, extra_data: RpcExtraData::from_iter(item.extra_data.bytes()) }
602});
603try_from!(item: &protowire::GetBlockTemplateResponseMessage, RpcResult<kaspa_rpc_core::GetBlockTemplateResponse>, {
604    Self {
605        block: item
606            .block
607            .as_ref()
608            .ok_or_else(|| RpcError::MissingRpcFieldError("GetBlockTemplateResponseMessage".to_string(), "block".to_string()))?
609            .try_into()?,
610        is_synced: item.is_synced,
611    }
612});
613
614try_from!(item: &protowire::GetBlockRequestMessage, kaspa_rpc_core::GetBlockRequest, {
615    Self { hash: RpcHash::from_str(&item.hash)?, include_transactions: item.include_transactions }
616});
617try_from!(item: &protowire::GetBlockResponseMessage, RpcResult<kaspa_rpc_core::GetBlockResponse>, {
618    Self {
619        block: item
620            .block
621            .as_ref()
622            .ok_or_else(|| RpcError::MissingRpcFieldError("GetBlockResponseMessage".to_string(), "block".to_string()))?
623            .try_into()?,
624    }
625});
626
627try_from!(item: &protowire::NotifyBlockAddedRequestMessage, kaspa_rpc_core::NotifyBlockAddedRequest, {
628    Self { command: item.command.into() }
629});
630try_from!(&protowire::NotifyBlockAddedResponseMessage, RpcResult<kaspa_rpc_core::NotifyBlockAddedResponse>);
631
632try_from!(&protowire::GetInfoRequestMessage, kaspa_rpc_core::GetInfoRequest);
633try_from!(item: &protowire::GetInfoResponseMessage, RpcResult<kaspa_rpc_core::GetInfoResponse>, {
634    Self {
635        p2p_id: item.p2p_id.clone(),
636        mempool_size: item.mempool_size,
637        server_version: item.server_version.clone(),
638        is_utxo_indexed: item.is_utxo_indexed,
639        is_synced: item.is_synced,
640        has_notify_command: item.has_notify_command,
641        has_message_id: item.has_message_id,
642    }
643});
644
645try_from!(item: &protowire::NotifyNewBlockTemplateRequestMessage, kaspa_rpc_core::NotifyNewBlockTemplateRequest, {
646    Self { command: item.command.into() }
647});
648try_from!(&protowire::NotifyNewBlockTemplateResponseMessage, RpcResult<kaspa_rpc_core::NotifyNewBlockTemplateResponse>);
649
650// ~~~
651
652try_from!(&protowire::GetCurrentNetworkRequestMessage, kaspa_rpc_core::GetCurrentNetworkRequest);
653try_from!(item: &protowire::GetCurrentNetworkResponseMessage, RpcResult<kaspa_rpc_core::GetCurrentNetworkResponse>, {
654    // Note that current_network is first converted to lowercase because the golang implementation
655    // returns a "human readable" version with a capital first letter while the rusty version
656    // is fully lowercase.
657    Self { network: RpcNetworkType::from_str(&item.current_network.to_lowercase())? }
658});
659
660try_from!(&protowire::GetPeerAddressesRequestMessage, kaspa_rpc_core::GetPeerAddressesRequest);
661try_from!(item: &protowire::GetPeerAddressesResponseMessage, RpcResult<kaspa_rpc_core::GetPeerAddressesResponse>, {
662    Self {
663        known_addresses: item.addresses.iter().map(RpcPeerAddress::try_from).collect::<Result<Vec<_>, _>>()?,
664        banned_addresses: item.banned_addresses.iter().map(RpcIpAddress::try_from).collect::<Result<Vec<_>, _>>()?,
665    }
666});
667
668try_from!(&protowire::GetSinkRequestMessage, kaspa_rpc_core::GetSinkRequest);
669try_from!(item: &protowire::GetSinkResponseMessage, RpcResult<kaspa_rpc_core::GetSinkResponse>, {
670    Self { sink: RpcHash::from_str(&item.sink)? }
671});
672
673try_from!(item: &protowire::GetMempoolEntryRequestMessage, kaspa_rpc_core::GetMempoolEntryRequest, {
674    Self {
675        transaction_id: kaspa_rpc_core::RpcTransactionId::from_str(&item.tx_id)?,
676        include_orphan_pool: item.include_orphan_pool,
677        filter_transaction_pool: item.filter_transaction_pool,
678    }
679});
680try_from!(item: &protowire::GetMempoolEntryResponseMessage, RpcResult<kaspa_rpc_core::GetMempoolEntryResponse>, {
681    Self {
682        mempool_entry: item
683            .entry
684            .as_ref()
685            .ok_or_else(|| RpcError::MissingRpcFieldError("GetMempoolEntryResponseMessage".to_string(), "entry".to_string()))?
686            .try_into()?,
687    }
688});
689
690try_from!(item: &protowire::GetMempoolEntriesRequestMessage, kaspa_rpc_core::GetMempoolEntriesRequest, {
691    Self { include_orphan_pool: item.include_orphan_pool, filter_transaction_pool: item.filter_transaction_pool }
692});
693try_from!(item: &protowire::GetMempoolEntriesResponseMessage, RpcResult<kaspa_rpc_core::GetMempoolEntriesResponse>, {
694    Self { mempool_entries: item.entries.iter().map(kaspa_rpc_core::RpcMempoolEntry::try_from).collect::<Result<Vec<_>, _>>()? }
695});
696
697try_from!(&protowire::GetConnectedPeerInfoRequestMessage, kaspa_rpc_core::GetConnectedPeerInfoRequest);
698try_from!(item: &protowire::GetConnectedPeerInfoResponseMessage, RpcResult<kaspa_rpc_core::GetConnectedPeerInfoResponse>, {
699    Self { peer_info: item.infos.iter().map(kaspa_rpc_core::RpcPeerInfo::try_from).collect::<Result<Vec<_>, _>>()? }
700});
701
702try_from!(item: &protowire::AddPeerRequestMessage, kaspa_rpc_core::AddPeerRequest, {
703    Self { peer_address: RpcContextualPeerAddress::from_str(&item.address)?, is_permanent: item.is_permanent }
704});
705try_from!(&protowire::AddPeerResponseMessage, RpcResult<kaspa_rpc_core::AddPeerResponse>);
706
707try_from!(item: &protowire::SubmitTransactionRequestMessage, kaspa_rpc_core::SubmitTransactionRequest, {
708    Self {
709        transaction: item
710            .transaction
711            .as_ref()
712            .ok_or_else(|| RpcError::MissingRpcFieldError("SubmitTransactionRequestMessage".to_string(), "transaction".to_string()))?
713            .try_into()?,
714        allow_orphan: item.allow_orphan,
715    }
716});
717try_from!(item: &protowire::SubmitTransactionResponseMessage, RpcResult<kaspa_rpc_core::SubmitTransactionResponse>, {
718    Self { transaction_id: RpcHash::from_str(&item.transaction_id)? }
719});
720
721try_from!(item: &protowire::SubmitTransactionReplacementRequestMessage, kaspa_rpc_core::SubmitTransactionReplacementRequest, {
722    Self {
723        transaction: item
724            .transaction
725            .as_ref()
726            .ok_or_else(|| RpcError::MissingRpcFieldError("SubmitTransactionReplacementRequestMessage".to_string(), "transaction".to_string()))?
727            .try_into()?,
728    }
729});
730try_from!(item: &protowire::SubmitTransactionReplacementResponseMessage, RpcResult<kaspa_rpc_core::SubmitTransactionReplacementResponse>, {
731    Self {
732        transaction_id: RpcHash::from_str(&item.transaction_id)?,
733        replaced_transaction: item
734            .replaced_transaction
735            .as_ref()
736            .ok_or_else(|| RpcError::MissingRpcFieldError("SubmitTransactionReplacementRequestMessage".to_string(), "replaced_transaction".to_string()))?
737            .try_into()?,
738    }
739});
740
741try_from!(item: &protowire::GetSubnetworkRequestMessage, kaspa_rpc_core::GetSubnetworkRequest, {
742    Self { subnetwork_id: kaspa_rpc_core::RpcSubnetworkId::from_str(&item.subnetwork_id)? }
743});
744try_from!(item: &protowire::GetSubnetworkResponseMessage, RpcResult<kaspa_rpc_core::GetSubnetworkResponse>, {
745    Self { gas_limit: item.gas_limit }
746});
747
748try_from!(item: &protowire::GetVirtualChainFromBlockRequestMessage, kaspa_rpc_core::GetVirtualChainFromBlockRequest, {
749    Self { start_hash: RpcHash::from_str(&item.start_hash)?, include_accepted_transaction_ids: item.include_accepted_transaction_ids }
750});
751try_from!(item: &protowire::GetVirtualChainFromBlockResponseMessage, RpcResult<kaspa_rpc_core::GetVirtualChainFromBlockResponse>, {
752    Self {
753        removed_chain_block_hashes: item
754            .removed_chain_block_hashes
755            .iter()
756            .map(|x| RpcHash::from_str(x))
757            .collect::<Result<Vec<_>, _>>()?,
758        added_chain_block_hashes: item.added_chain_block_hashes.iter().map(|x| RpcHash::from_str(x)).collect::<Result<Vec<_>, _>>()?,
759        accepted_transaction_ids: item.accepted_transaction_ids.iter().map(|x| x.try_into()).collect::<Result<Vec<_>, _>>()?,
760    }
761});
762
763try_from!(item: &protowire::GetBlocksRequestMessage, kaspa_rpc_core::GetBlocksRequest, {
764    Self {
765        low_hash: if item.low_hash.is_empty() { None } else { Some(RpcHash::from_str(&item.low_hash)?) },
766        include_blocks: item.include_blocks,
767        include_transactions: item.include_transactions,
768    }
769});
770try_from!(item: &protowire::GetBlocksResponseMessage, RpcResult<kaspa_rpc_core::GetBlocksResponse>, {
771    Self {
772        block_hashes: item.block_hashes.iter().map(|x| RpcHash::from_str(x)).collect::<Result<Vec<_>, _>>()?,
773        blocks: item.blocks.iter().map(|x| x.try_into()).collect::<Result<Vec<_>, _>>()?,
774    }
775});
776
777try_from!(&protowire::GetBlockCountRequestMessage, kaspa_rpc_core::GetBlockCountRequest);
778try_from!(item: &protowire::GetBlockCountResponseMessage, RpcResult<kaspa_rpc_core::GetBlockCountResponse>, {
779    Self { header_count: item.header_count, block_count: item.block_count }
780});
781
782try_from!(&protowire::GetBlockDagInfoRequestMessage, kaspa_rpc_core::GetBlockDagInfoRequest);
783try_from!(item: &protowire::GetBlockDagInfoResponseMessage, RpcResult<kaspa_rpc_core::GetBlockDagInfoResponse>, {
784    Self {
785        network: kaspa_rpc_core::RpcNetworkId::from_prefixed(&item.network_name)?,
786        block_count: item.block_count,
787        header_count: item.header_count,
788        tip_hashes: item.tip_hashes.iter().map(|x| RpcHash::from_str(x)).collect::<Result<Vec<_>, _>>()?,
789        difficulty: item.difficulty,
790        past_median_time: item.past_median_time as u64,
791        virtual_parent_hashes: item.virtual_parent_hashes.iter().map(|x| RpcHash::from_str(x)).collect::<Result<Vec<_>, _>>()?,
792        pruning_point_hash: RpcHash::from_str(&item.pruning_point_hash)?,
793        virtual_daa_score: item.virtual_daa_score,
794        sink: item.sink.parse()?,
795    }
796});
797
798try_from!(item: &protowire::ResolveFinalityConflictRequestMessage, kaspa_rpc_core::ResolveFinalityConflictRequest, {
799    Self { finality_block_hash: RpcHash::from_str(&item.finality_block_hash)? }
800});
801try_from!(&protowire::ResolveFinalityConflictResponseMessage, RpcResult<kaspa_rpc_core::ResolveFinalityConflictResponse>);
802
803try_from!(&protowire::ShutdownRequestMessage, kaspa_rpc_core::ShutdownRequest);
804try_from!(&protowire::ShutdownResponseMessage, RpcResult<kaspa_rpc_core::ShutdownResponse>);
805
806try_from!(item: &protowire::GetHeadersRequestMessage, kaspa_rpc_core::GetHeadersRequest, {
807    Self { start_hash: RpcHash::from_str(&item.start_hash)?, limit: item.limit, is_ascending: item.is_ascending }
808});
809try_from!(item: &protowire::GetHeadersResponseMessage, RpcResult<kaspa_rpc_core::GetHeadersResponse>, {
810    // TODO
811    Self { headers: vec![] }
812});
813
814try_from!(item: &protowire::GetUtxosByAddressesRequestMessage, kaspa_rpc_core::GetUtxosByAddressesRequest, {
815    Self { addresses: item.addresses.iter().map(|x| x.as_str().try_into()).collect::<Result<Vec<_>, _>>()? }
816});
817try_from!(item: &protowire::GetUtxosByAddressesResponseMessage, RpcResult<kaspa_rpc_core::GetUtxosByAddressesResponse>, {
818    Self { entries: item.entries.iter().map(|x| x.try_into()).collect::<Result<Vec<_>, _>>()? }
819});
820
821try_from!(item: &protowire::GetBalanceByAddressRequestMessage, kaspa_rpc_core::GetBalanceByAddressRequest, {
822    Self { address: item.address.as_str().try_into()? }
823});
824try_from!(item: &protowire::GetBalanceByAddressResponseMessage, RpcResult<kaspa_rpc_core::GetBalanceByAddressResponse>, {
825    Self { balance: item.balance }
826});
827
828try_from!(item: &protowire::GetBalancesByAddressesRequestMessage, kaspa_rpc_core::GetBalancesByAddressesRequest, {
829    Self { addresses: item.addresses.iter().map(|x| x.as_str().try_into()).collect::<Result<Vec<_>, _>>()? }
830});
831try_from!(item: &protowire::GetBalancesByAddressesResponseMessage, RpcResult<kaspa_rpc_core::GetBalancesByAddressesResponse>, {
832    Self { entries: item.entries.iter().map(|x| x.try_into()).collect::<Result<Vec<_>, _>>()? }
833});
834
835try_from!(&protowire::GetSinkBlueScoreRequestMessage, kaspa_rpc_core::GetSinkBlueScoreRequest);
836try_from!(item: &protowire::GetSinkBlueScoreResponseMessage, RpcResult<kaspa_rpc_core::GetSinkBlueScoreResponse>, {
837    Self { blue_score: item.blue_score }
838});
839
840try_from!(item: &protowire::BanRequestMessage, kaspa_rpc_core::BanRequest, { Self { ip: RpcIpAddress::from_str(&item.ip)? } });
841try_from!(&protowire::BanResponseMessage, RpcResult<kaspa_rpc_core::BanResponse>);
842
843try_from!(item: &protowire::UnbanRequestMessage, kaspa_rpc_core::UnbanRequest, { Self { ip: RpcIpAddress::from_str(&item.ip)? } });
844try_from!(&protowire::UnbanResponseMessage, RpcResult<kaspa_rpc_core::UnbanResponse>);
845
846try_from!(item: &protowire::EstimateNetworkHashesPerSecondRequestMessage, kaspa_rpc_core::EstimateNetworkHashesPerSecondRequest, {
847    Self {
848        window_size: item.window_size,
849        start_hash: if item.start_hash.is_empty() { None } else { Some(RpcHash::from_str(&item.start_hash)?) },
850    }
851});
852try_from!(
853    item: &protowire::EstimateNetworkHashesPerSecondResponseMessage,
854    RpcResult<kaspa_rpc_core::EstimateNetworkHashesPerSecondResponse>,
855    { Self { network_hashes_per_second: item.network_hashes_per_second } }
856);
857
858try_from!(item: &protowire::GetMempoolEntriesByAddressesRequestMessage, kaspa_rpc_core::GetMempoolEntriesByAddressesRequest, {
859    Self {
860        addresses: item.addresses.iter().map(|x| x.as_str().try_into()).collect::<Result<Vec<_>, _>>()?,
861        include_orphan_pool: item.include_orphan_pool,
862        filter_transaction_pool: item.filter_transaction_pool,
863    }
864});
865try_from!(
866    item: &protowire::GetMempoolEntriesByAddressesResponseMessage,
867    RpcResult<kaspa_rpc_core::GetMempoolEntriesByAddressesResponse>,
868    { Self { entries: item.entries.iter().map(|x| x.try_into()).collect::<Result<Vec<_>, _>>()? } }
869);
870
871try_from!(&protowire::GetCoinSupplyRequestMessage, kaspa_rpc_core::GetCoinSupplyRequest);
872try_from!(item: &protowire::GetCoinSupplyResponseMessage, RpcResult<kaspa_rpc_core::GetCoinSupplyResponse>, {
873    Self { max_sompi: item.max_sompi, circulating_sompi: item.circulating_sompi }
874});
875
876try_from!(item: &protowire::GetDaaScoreTimestampEstimateRequestMessage, kaspa_rpc_core::GetDaaScoreTimestampEstimateRequest , {
877    Self {
878        daa_scores: item.daa_scores.clone()
879    }
880});
881try_from!(item: &protowire::GetDaaScoreTimestampEstimateResponseMessage, RpcResult<kaspa_rpc_core::GetDaaScoreTimestampEstimateResponse>, {
882    Self { timestamps: item.timestamps.clone() }
883});
884
885try_from!(&protowire::GetFeeEstimateRequestMessage, kaspa_rpc_core::GetFeeEstimateRequest);
886try_from!(item: &protowire::GetFeeEstimateResponseMessage, RpcResult<kaspa_rpc_core::GetFeeEstimateResponse>, {
887    Self {
888        estimate: item.estimate
889            .as_ref()
890            .ok_or_else(|| RpcError::MissingRpcFieldError("GetFeeEstimateResponseMessage".to_string(), "estimate".to_string()))?
891            .try_into()?
892    }
893});
894try_from!(item: &protowire::GetFeeEstimateExperimentalRequestMessage, kaspa_rpc_core::GetFeeEstimateExperimentalRequest, {
895    Self {
896        verbose: item.verbose
897    }
898});
899try_from!(item: &protowire::GetFeeEstimateExperimentalResponseMessage, RpcResult<kaspa_rpc_core::GetFeeEstimateExperimentalResponse>, {
900    Self {
901        estimate: item.estimate
902            .as_ref()
903            .ok_or_else(|| RpcError::MissingRpcFieldError("GetFeeEstimateExperimentalResponseMessage".to_string(), "estimate".to_string()))?
904            .try_into()?,
905        verbose: item.verbose.as_ref().map(|x| x.try_into()).transpose()?
906    }
907});
908
909try_from!(item: &protowire::GetCurrentBlockColorRequestMessage, kaspa_rpc_core::GetCurrentBlockColorRequest, {
910    Self {
911        hash: RpcHash::from_str(&item.hash)?
912    }
913});
914try_from!(item: &protowire::GetCurrentBlockColorResponseMessage, RpcResult<kaspa_rpc_core::GetCurrentBlockColorResponse>, {
915    Self {
916        blue: item.blue
917    }
918});
919
920try_from!(&protowire::PingRequestMessage, kaspa_rpc_core::PingRequest);
921try_from!(&protowire::PingResponseMessage, RpcResult<kaspa_rpc_core::PingResponse>);
922
923try_from!(item: &protowire::GetMetricsRequestMessage, kaspa_rpc_core::GetMetricsRequest, {
924    Self {
925        process_metrics: item.process_metrics,
926        connection_metrics: item.connection_metrics,
927        bandwidth_metrics:item.bandwidth_metrics,
928        consensus_metrics: item.consensus_metrics,
929        storage_metrics: item.storage_metrics,
930        custom_metrics : item.custom_metrics,
931    }
932});
933try_from!(item: &protowire::GetMetricsResponseMessage, RpcResult<kaspa_rpc_core::GetMetricsResponse>, {
934    Self {
935        server_time: item.server_time,
936        process_metrics: item.process_metrics.as_ref().map(|x| x.try_into()).transpose()?,
937        connection_metrics: item.connection_metrics.as_ref().map(|x| x.try_into()).transpose()?,
938        bandwidth_metrics: item.bandwidth_metrics.as_ref().map(|x| x.try_into()).transpose()?,
939        consensus_metrics: item.consensus_metrics.as_ref().map(|x| x.try_into()).transpose()?,
940        storage_metrics: item.storage_metrics.as_ref().map(|x| x.try_into()).transpose()?,
941        // TODO
942        custom_metrics: None,
943    }
944});
945
946try_from!(item: &protowire::GetConnectionsRequestMessage, kaspa_rpc_core::GetConnectionsRequest, {
947    Self { include_profile_data : item.include_profile_data }
948});
949try_from!(item: &protowire::GetConnectionsResponseMessage, RpcResult<kaspa_rpc_core::GetConnectionsResponse>, {
950    Self {
951        clients: item.clients,
952        peers: item.peers as u16,
953        profile_data: item.profile_data.as_ref().map(|x| x.try_into()).transpose()?,
954    }
955});
956
957try_from!(&protowire::GetSystemInfoRequestMessage, kaspa_rpc_core::GetSystemInfoRequest);
958try_from!(item: &protowire::GetSystemInfoResponseMessage, RpcResult<kaspa_rpc_core::GetSystemInfoResponse>, {
959    Self {
960        version: item.version.clone(),
961        system_id: (!item.system_id.is_empty()).then(|| FromHex::from_hex(&item.system_id)).transpose()?,
962        git_hash: (!item.git_hash.is_empty()).then(|| FromHex::from_hex(&item.git_hash)).transpose()?,
963        total_memory: item.total_memory,
964        cpu_physical_cores: item.core_num as u16,
965        fd_limit: item.fd_limit,
966        proxy_socket_limit_per_cpu_core : (item.proxy_socket_limit_per_cpu_core > 0).then_some(item.proxy_socket_limit_per_cpu_core),
967    }
968});
969
970try_from!(&protowire::GetServerInfoRequestMessage, kaspa_rpc_core::GetServerInfoRequest);
971try_from!(item: &protowire::GetServerInfoResponseMessage, RpcResult<kaspa_rpc_core::GetServerInfoResponse>, {
972    Self {
973        rpc_api_version: item.rpc_api_version as u16,
974        rpc_api_revision: item.rpc_api_revision as u16,
975        server_version: item.server_version.clone(),
976        network_id: NetworkId::from_str(&item.network_id)?,
977        has_utxo_index: item.has_utxo_index,
978        is_synced: item.is_synced,
979        virtual_daa_score: item.virtual_daa_score,
980    }
981});
982
983try_from!(&protowire::GetSyncStatusRequestMessage, kaspa_rpc_core::GetSyncStatusRequest);
984try_from!(item: &protowire::GetSyncStatusResponseMessage, RpcResult<kaspa_rpc_core::GetSyncStatusResponse>, {
985    Self {
986        is_synced: item.is_synced,
987    }
988});
989
990try_from!(item: &protowire::NotifyUtxosChangedRequestMessage, kaspa_rpc_core::NotifyUtxosChangedRequest, {
991    Self {
992        addresses: item.addresses.iter().map(|x| x.as_str().try_into()).collect::<Result<Vec<_>, _>>()?,
993        command: item.command.into(),
994    }
995});
996try_from!(item: &protowire::StopNotifyingUtxosChangedRequestMessage, kaspa_rpc_core::NotifyUtxosChangedRequest, {
997    Self {
998        addresses: item.addresses.iter().map(|x| x.as_str().try_into()).collect::<Result<Vec<_>, _>>()?,
999        command: Command::Stop,
1000    }
1001});
1002try_from!(&protowire::NotifyUtxosChangedResponseMessage, RpcResult<kaspa_rpc_core::NotifyUtxosChangedResponse>);
1003try_from!(&protowire::StopNotifyingUtxosChangedResponseMessage, RpcResult<kaspa_rpc_core::NotifyUtxosChangedResponse>);
1004
1005try_from!(
1006    item: &protowire::NotifyPruningPointUtxoSetOverrideRequestMessage,
1007    kaspa_rpc_core::NotifyPruningPointUtxoSetOverrideRequest,
1008    { Self { command: item.command.into() } }
1009);
1010try_from!(
1011    _item: &protowire::StopNotifyingPruningPointUtxoSetOverrideRequestMessage,
1012    kaspa_rpc_core::NotifyPruningPointUtxoSetOverrideRequest,
1013    { Self { command: Command::Stop } }
1014);
1015try_from!(
1016    &protowire::NotifyPruningPointUtxoSetOverrideResponseMessage,
1017    RpcResult<kaspa_rpc_core::NotifyPruningPointUtxoSetOverrideResponse>
1018);
1019try_from!(
1020    &protowire::StopNotifyingPruningPointUtxoSetOverrideResponseMessage,
1021    RpcResult<kaspa_rpc_core::NotifyPruningPointUtxoSetOverrideResponse>
1022);
1023
1024try_from!(item: &protowire::NotifyFinalityConflictRequestMessage, kaspa_rpc_core::NotifyFinalityConflictRequest, {
1025    Self { command: item.command.into() }
1026});
1027try_from!(&protowire::NotifyFinalityConflictResponseMessage, RpcResult<kaspa_rpc_core::NotifyFinalityConflictResponse>);
1028
1029try_from!(item: &protowire::NotifyVirtualDaaScoreChangedRequestMessage, kaspa_rpc_core::NotifyVirtualDaaScoreChangedRequest, {
1030    Self { command: item.command.into() }
1031});
1032try_from!(&protowire::NotifyVirtualDaaScoreChangedResponseMessage, RpcResult<kaspa_rpc_core::NotifyVirtualDaaScoreChangedResponse>);
1033
1034try_from!(item: &protowire::NotifyVirtualChainChangedRequestMessage, kaspa_rpc_core::NotifyVirtualChainChangedRequest, {
1035    Self { include_accepted_transaction_ids: item.include_accepted_transaction_ids, command: item.command.into() }
1036});
1037try_from!(&protowire::NotifyVirtualChainChangedResponseMessage, RpcResult<kaspa_rpc_core::NotifyVirtualChainChangedResponse>);
1038
1039try_from!(item: &protowire::NotifySinkBlueScoreChangedRequestMessage, kaspa_rpc_core::NotifySinkBlueScoreChangedRequest, {
1040    Self { command: item.command.into() }
1041});
1042try_from!(&protowire::NotifySinkBlueScoreChangedResponseMessage, RpcResult<kaspa_rpc_core::NotifySinkBlueScoreChangedResponse>);
1043
1044// ----------------------------------------------------------------------------
1045// Unit tests
1046// ----------------------------------------------------------------------------
1047
1048// TODO: tests
1049
1050#[cfg(test)]
1051mod tests {
1052    use kaspa_rpc_core::{RpcError, RpcResult, SubmitBlockRejectReason, SubmitBlockReport, SubmitBlockResponse};
1053
1054    use crate::protowire::{self, submit_block_response_message::RejectReason, SubmitBlockResponseMessage};
1055
1056    #[test]
1057    fn test_submit_block_response() {
1058        struct Test {
1059            rpc_core: RpcResult<kaspa_rpc_core::SubmitBlockResponse>,
1060            protowire: protowire::SubmitBlockResponseMessage,
1061        }
1062        impl Test {
1063            fn new(
1064                rpc_core: RpcResult<kaspa_rpc_core::SubmitBlockResponse>,
1065                protowire: protowire::SubmitBlockResponseMessage,
1066            ) -> Self {
1067                Self { rpc_core, protowire }
1068            }
1069        }
1070        let tests = vec![
1071            Test::new(
1072                Ok(SubmitBlockResponse { report: SubmitBlockReport::Success }),
1073                SubmitBlockResponseMessage { reject_reason: RejectReason::None as i32, error: None },
1074            ),
1075            Test::new(
1076                Ok(SubmitBlockResponse { report: SubmitBlockReport::Reject(SubmitBlockRejectReason::BlockInvalid) }),
1077                SubmitBlockResponseMessage {
1078                    reject_reason: RejectReason::BlockInvalid as i32,
1079                    error: Some(protowire::RpcError {
1080                        message: RpcError::SubmitBlockError(SubmitBlockRejectReason::BlockInvalid).to_string(),
1081                    }),
1082                },
1083            ),
1084            Test::new(
1085                Ok(SubmitBlockResponse { report: SubmitBlockReport::Reject(SubmitBlockRejectReason::IsInIBD) }),
1086                SubmitBlockResponseMessage {
1087                    reject_reason: RejectReason::IsInIbd as i32,
1088                    error: Some(protowire::RpcError {
1089                        message: RpcError::SubmitBlockError(SubmitBlockRejectReason::IsInIBD).to_string(),
1090                    }),
1091                },
1092            ),
1093            Test::new(
1094                Ok(SubmitBlockResponse { report: SubmitBlockReport::Reject(SubmitBlockRejectReason::RouteIsFull) }),
1095                SubmitBlockResponseMessage {
1096                    reject_reason: RejectReason::None as i32, // This rpc core reject reason has no matching protowire variant
1097                    error: Some(protowire::RpcError {
1098                        message: RpcError::SubmitBlockError(SubmitBlockRejectReason::RouteIsFull).to_string(),
1099                    }),
1100                },
1101            ),
1102        ];
1103
1104        for test in tests {
1105            let cnv_protowire: SubmitBlockResponseMessage = test.rpc_core.as_ref().map_err(|x| x.clone()).into();
1106            assert_eq!(cnv_protowire.reject_reason, test.protowire.reject_reason);
1107            assert_eq!(cnv_protowire.error.is_some(), test.protowire.error.is_some());
1108            assert_eq!(cnv_protowire.error, test.protowire.error);
1109
1110            let cnv_rpc_core: RpcResult<SubmitBlockResponse> = (&test.protowire).try_into();
1111            assert_eq!(cnv_rpc_core.is_ok(), test.rpc_core.is_ok());
1112            match cnv_rpc_core {
1113                Ok(ref cnv_response) => {
1114                    let Ok(ref response) = test.rpc_core else { panic!() };
1115                    assert_eq!(cnv_response.report, response.report);
1116                }
1117                Err(ref cnv_err) => {
1118                    let Err(ref err) = test.rpc_core else { panic!() };
1119                    assert_eq!(cnv_err.to_string(), err.to_string());
1120                }
1121            }
1122        }
1123    }
1124}