ckb_cli_plugin_protocol/
convert.rs

1use ckb_jsonrpc_types::{JsonBytes, Transaction};
2use faster_hex::{hex_decode, hex_string};
3use serde::de::DeserializeOwned;
4use std::convert::TryFrom;
5use std::str::FromStr;
6
7use super::{
8    method, CallbackRequest, IndexerRequest, JsonrpcRequest, JsonrpcResponse, KeyStoreRequest,
9    LiveCellIndexType, PluginRequest, PluginResponse, RpcRequest, JSONRPC_VERSION,
10};
11
12impl From<(u64, PluginRequest)> for JsonrpcRequest {
13    fn from((id, request): (u64, PluginRequest)) -> JsonrpcRequest {
14        let (method, params) = match request {
15            PluginRequest::Quit => (method::QUIT, Vec::new()),
16            PluginRequest::GetConfig => (method::GET_CONFIG, Vec::new()),
17            PluginRequest::ReadPassword(prompt) => {
18                (method::READ_PASSWORD, vec![serde_json::json!(prompt)])
19            }
20            PluginRequest::PrintStdout(content) => {
21                (method::PRINT_STDOUT, vec![serde_json::json!(content)])
22            }
23            PluginRequest::PrintStderr(content) => {
24                (method::PRINT_STDERR, vec![serde_json::json!(content)])
25            }
26            PluginRequest::RpcUrlChanged(url) => {
27                (method::RPC_URL_CHANGED, vec![serde_json::json!(url)])
28            }
29            PluginRequest::SubCommand(args) => (method::SUB_COMMAND, vec![serde_json::json!(args)]),
30            PluginRequest::Callback(callback_request) => callback_request.into(),
31            PluginRequest::Rpc(rpc_request) => rpc_request.into(),
32            PluginRequest::Indexer {
33                genesis_hash,
34                request: indexer_request,
35            } => {
36                let (method, mut params) = indexer_request.into();
37                params.insert(0, serde_json::json!(genesis_hash));
38                (method, params)
39            }
40            PluginRequest::KeyStore(keystore_request) => keystore_request.into(),
41        };
42        JsonrpcRequest {
43            jsonrpc: JSONRPC_VERSION.to_string(),
44            id: serde_json::json!(id),
45            method: method.to_string(),
46            params,
47        }
48    }
49}
50
51impl TryFrom<JsonrpcRequest> for (u64, PluginRequest) {
52    type Error = String;
53    fn try_from(data: JsonrpcRequest) -> Result<(u64, PluginRequest), Self::Error> {
54        if data.jsonrpc != JSONRPC_VERSION {
55            return Err(format!("Invalid jsonrpc field: {}", data.jsonrpc));
56        }
57        let request_id: u64 = serde_json::from_value(data.id.clone())
58            .map_err(|err| format!("Request id must be integer number, error={}", err))?;
59
60        let request = match data.method.as_str() {
61            method::QUIT => PluginRequest::Quit,
62            method::GET_CONFIG => PluginRequest::GetConfig,
63            method::READ_PASSWORD => PluginRequest::ReadPassword(parse_param(&data, 0, "prompt")?),
64            method::PRINT_STDOUT => PluginRequest::PrintStdout(parse_param(&data, 0, "content")?),
65            method::PRINT_STDERR => PluginRequest::PrintStderr(parse_param(&data, 0, "content")?),
66            method::RPC_URL_CHANGED => PluginRequest::RpcUrlChanged(parse_param(&data, 0, "url")?),
67            method::SUB_COMMAND => PluginRequest::SubCommand(parse_param(&data, 0, "args")?),
68            method if method.starts_with(method::CALLBACK_PREFIX) => {
69                CallbackRequest::try_from(&data).map(PluginRequest::Callback)?
70            }
71            method if method.starts_with(method::RPC_PREFIX) => {
72                RpcRequest::try_from(&data).map(PluginRequest::Rpc)?
73            }
74            method if method.starts_with(method::INDEXER_PREFIX) => {
75                let genesis_hash = parse_param(&data, 0, "genesis_hash")?;
76                IndexerRequest::try_from(&data).map(|request| PluginRequest::Indexer {
77                    genesis_hash,
78                    request,
79                })?
80            }
81            method if method.starts_with(method::KEYSTORE_PREFIX) => {
82                KeyStoreRequest::try_from(&data).map(PluginRequest::KeyStore)?
83            }
84            method => {
85                return Err(format!("Invalid request method: {}", method));
86            }
87        };
88        Ok((request_id, request))
89    }
90}
91
92impl From<CallbackRequest> for (&'static str, Vec<serde_json::Value>) {
93    fn from(request: CallbackRequest) -> (&'static str, Vec<serde_json::Value>) {
94        match request {
95            CallbackRequest::SendTransaction { tx, sub_command } => {
96                let params = vec![
97                    serde_json::to_value(&tx).expect("Serialize json failed"),
98                    serde_json::json!(sub_command),
99                ];
100                (method::CALLBACK_SEND_TRANSACTION, params)
101            }
102        }
103    }
104}
105impl TryFrom<&JsonrpcRequest> for CallbackRequest {
106    type Error = String;
107    fn try_from(data: &JsonrpcRequest) -> Result<CallbackRequest, Self::Error> {
108        let request = match data.method.as_str() {
109            method::CALLBACK_SEND_TRANSACTION => {
110                let tx: Transaction = parse_param(data, 0, "transaction")?;
111                let sub_command: String = parse_param(data, 1, "sub-command")?;
112                CallbackRequest::SendTransaction { tx, sub_command }
113            }
114            _ => {
115                return Err(format!("Invalid request method: {}", data.method));
116            }
117        };
118        Ok(request)
119    }
120}
121
122impl From<KeyStoreRequest> for (&'static str, Vec<serde_json::Value>) {
123    fn from(request: KeyStoreRequest) -> (&'static str, Vec<serde_json::Value>) {
124        match request {
125            KeyStoreRequest::ListAccount => (method::KEYSTORE_LIST_ACCOUNT, Vec::new()),
126            KeyStoreRequest::HasAccount(hash160) => (
127                method::KEYSTORE_HAS_ACCOUNT,
128                vec![serde_json::json!(hash160)],
129            ),
130            KeyStoreRequest::CreateAccount(password) => (
131                method::KEYSTORE_CREATE_ACCOUNT,
132                vec![serde_json::json!(password)],
133            ),
134            KeyStoreRequest::UpdatePassword {
135                hash160,
136                password,
137                new_password,
138            } => {
139                let params = vec![
140                    serde_json::json!(hash160),
141                    serde_json::json!(password),
142                    serde_json::json!(new_password),
143                ];
144                (method::KEYSTORE_UPDATE_PASSWORD, params)
145            }
146            KeyStoreRequest::Import {
147                privkey,
148                chain_code,
149                password,
150            } => {
151                let privkey = format!("0x{}", hex_string(&privkey));
152                let chain_code = format!("0x{}", hex_string(&chain_code));
153                let params = vec![
154                    serde_json::json!(privkey),
155                    serde_json::json!(chain_code),
156                    serde_json::json!(password),
157                ];
158                (method::KEYSTORE_IMPORT, params)
159            }
160            KeyStoreRequest::ImportAccount {
161                account_id,
162                password,
163            } => {
164                let account_id = format!("0x{}", hex_string(account_id.as_bytes()));
165                let params = vec![serde_json::json!(account_id), serde_json::json!(password)];
166                (method::KEYSTORE_IMPORT_ACCOUNT, params)
167            }
168            KeyStoreRequest::Export { hash160, password } => {
169                let params = vec![serde_json::json!(hash160), serde_json::json!(password)];
170                (method::KEYSTORE_EXPORT, params)
171            }
172            KeyStoreRequest::Sign {
173                hash160,
174                path,
175                message,
176                target,
177                recoverable,
178                password,
179            } => {
180                let params = vec![
181                    serde_json::json!(hash160),
182                    serde_json::json!(path),
183                    serde_json::json!(message),
184                    serde_json::json!(target),
185                    serde_json::json!(recoverable),
186                    serde_json::json!(password),
187                ];
188                (method::KEYSTORE_SIGN, params)
189            }
190            KeyStoreRequest::ExtendedPubkey {
191                hash160,
192                path,
193                password,
194            } => {
195                let params = vec![
196                    serde_json::json!(hash160),
197                    serde_json::json!(path),
198                    serde_json::json!(password),
199                ];
200                (method::KEYSTORE_EXTENDED_PUBKEY, params)
201            }
202            KeyStoreRequest::DerivedKeySet {
203                hash160,
204                external_max_len,
205                change_last,
206                change_max_len,
207                password,
208            } => {
209                let params = vec![
210                    serde_json::json!(hash160),
211                    serde_json::json!(external_max_len),
212                    serde_json::json!(change_last),
213                    serde_json::json!(change_max_len),
214                    serde_json::json!(password),
215                ];
216                (method::KEYSTORE_DERIVED_KEY_SET, params)
217            }
218            KeyStoreRequest::DerivedKeySetByIndex {
219                hash160,
220                external_start,
221                external_length,
222                change_start,
223                change_length,
224                password,
225            } => {
226                let params = vec![
227                    serde_json::json!(hash160),
228                    serde_json::json!(external_start),
229                    serde_json::json!(external_length),
230                    serde_json::json!(change_start),
231                    serde_json::json!(change_length),
232                    serde_json::json!(password),
233                ];
234                (method::KEYSTORE_DERIVED_KEY_SET_BY_INDEX, params)
235            }
236            KeyStoreRequest::Any(value) => (method::KEYSTORE_ANY, vec![value]),
237        }
238    }
239}
240impl TryFrom<&JsonrpcRequest> for KeyStoreRequest {
241    type Error = String;
242    fn try_from(data: &JsonrpcRequest) -> Result<KeyStoreRequest, Self::Error> {
243        let request = match data.method.as_str() {
244            method::KEYSTORE_LIST_ACCOUNT => KeyStoreRequest::ListAccount,
245            method::KEYSTORE_HAS_ACCOUNT => {
246                KeyStoreRequest::HasAccount(parse_param(data, 0, "hash160")?)
247            }
248            method::KEYSTORE_CREATE_ACCOUNT => {
249                KeyStoreRequest::CreateAccount(parse_param(data, 0, "password")?)
250            }
251            method::KEYSTORE_UPDATE_PASSWORD => KeyStoreRequest::UpdatePassword {
252                hash160: parse_param(data, 0, "hash160")?,
253                password: parse_param(data, 1, "hash160")?,
254                new_password: parse_param(data, 2, "hash160")?,
255            },
256            method::KEYSTORE_IMPORT => KeyStoreRequest::Import {
257                privkey: parse_h256(data, 0, "privkey")?,
258                chain_code: parse_h256(data, 1, "chain_code")?,
259                password: parse_param(data, 2, "password")?,
260            },
261            method::KEYSTORE_IMPORT_ACCOUNT => KeyStoreRequest::ImportAccount {
262                account_id: JsonBytes::from_vec(parse_bytes(data, 0, "account_id")?),
263                password: parse_param(data, 1, "password")?,
264            },
265            method::KEYSTORE_EXPORT => KeyStoreRequest::Export {
266                hash160: parse_param(data, 0, "hash160")?,
267                password: parse_param(data, 1, "password")?,
268            },
269            method::KEYSTORE_SIGN => KeyStoreRequest::Sign {
270                hash160: parse_param(data, 0, "hash160")?,
271                path: parse_param(data, 1, "path")?,
272                message: parse_param(data, 2, "message")?,
273                target: parse_param(data, 3, "target")?,
274                recoverable: parse_param(data, 4, "recoverable")?,
275                password: parse_param(data, 5, "password")?,
276            },
277            method::KEYSTORE_EXTENDED_PUBKEY => KeyStoreRequest::ExtendedPubkey {
278                hash160: parse_param(data, 0, "hash160")?,
279                path: parse_param(data, 1, "path")?,
280                password: parse_param(data, 2, "password")?,
281            },
282            method::KEYSTORE_DERIVED_KEY_SET => KeyStoreRequest::DerivedKeySet {
283                hash160: parse_param(data, 0, "hash160")?,
284                external_max_len: parse_param(data, 1, "external_max_len")?,
285                change_last: parse_param(data, 2, "change_last")?,
286                change_max_len: parse_param(data, 3, "change_max_len")?,
287                password: parse_param(data, 4, "password")?,
288            },
289            method::KEYSTORE_DERIVED_KEY_SET_BY_INDEX => KeyStoreRequest::DerivedKeySetByIndex {
290                hash160: parse_param(data, 0, "hash160")?,
291                external_start: parse_param(data, 1, "external_start")?,
292                external_length: parse_param(data, 2, "external_length")?,
293                change_start: parse_param(data, 3, "change_start")?,
294                change_length: parse_param(data, 4, "change_length")?,
295                password: parse_param(data, 5, "password")?,
296            },
297            method::KEYSTORE_ANY => KeyStoreRequest::Any(parse_param(data, 0, "value")?),
298            _ => {
299                return Err(format!("Invalid request method: {}", data.method));
300            }
301        };
302        Ok(request)
303    }
304}
305
306impl From<RpcRequest> for (&'static str, Vec<serde_json::Value>) {
307    fn from(request: RpcRequest) -> (&'static str, Vec<serde_json::Value>) {
308        match request {
309            RpcRequest::GetBlock { hash } => (method::RPC_GET_BLOCK, vec![serde_json::json!(hash)]),
310            RpcRequest::GetBlockByNumber { number } => (
311                method::RPC_GET_BLOCK_BY_NUMBER,
312                vec![serde_json::json!(number)],
313            ),
314            RpcRequest::GetBlockHash { number } => {
315                (method::RPC_GET_BLOCK_HASH, vec![serde_json::json!(number)])
316            }
317        }
318    }
319}
320impl TryFrom<&JsonrpcRequest> for RpcRequest {
321    type Error = String;
322    fn try_from(data: &JsonrpcRequest) -> Result<RpcRequest, Self::Error> {
323        let request = match data.method.as_str() {
324            method::RPC_GET_BLOCK => RpcRequest::GetBlock {
325                hash: parse_param(data, 0, "hash")?,
326            },
327            method::RPC_GET_BLOCK_BY_NUMBER => RpcRequest::GetBlockByNumber {
328                number: parse_param(data, 0, "number")?,
329            },
330            method::RPC_GET_BLOCK_HASH => RpcRequest::GetBlockHash {
331                number: parse_param(data, 0, "number")?,
332            },
333            _ => {
334                return Err(format!("Invalid request method: {}", data.method));
335            }
336        };
337        Ok(request)
338    }
339}
340
341impl From<IndexerRequest> for (&'static str, Vec<serde_json::Value>) {
342    fn from(request: IndexerRequest) -> (&'static str, Vec<serde_json::Value>) {
343        match request {
344            IndexerRequest::TipHeader => (method::INDEXER_TIP_HEADER, Vec::new()),
345            IndexerRequest::LastHeader => (method::INDEXER_LAST_HEADER, Vec::new()),
346            IndexerRequest::GetCapacity(lock_hash) => (
347                method::INDEXER_GET_CAPACITY,
348                vec![serde_json::json!(lock_hash)],
349            ),
350            IndexerRequest::LiveCells {
351                index,
352                hash,
353                from_number,
354                to_number,
355                limit,
356            } => {
357                let params = vec![
358                    serde_json::json!(index.to_string()),
359                    serde_json::json!(hash),
360                    serde_json::json!(from_number),
361                    serde_json::json!(to_number),
362                    serde_json::json!(limit),
363                ];
364                (method::INDEXER_GET_LIVE_CELLS, params)
365            }
366            IndexerRequest::TopN(n) => (method::INDEXER_GET_TOPN, vec![serde_json::json!(n)]),
367            IndexerRequest::IndexerInfo => (method::INDEXER_GET_INDEXER_INFO, Vec::new()),
368            IndexerRequest::Any(value) => (method::INDEXER_ANY, vec![value]),
369        }
370    }
371}
372impl TryFrom<&JsonrpcRequest> for IndexerRequest {
373    type Error = String;
374    fn try_from(data: &JsonrpcRequest) -> Result<IndexerRequest, Self::Error> {
375        // NOTE: the first parameter is genesis_hash
376        let request = match data.method.as_str() {
377            method::INDEXER_TIP_HEADER => IndexerRequest::TipHeader,
378            method::INDEXER_LAST_HEADER => IndexerRequest::LastHeader,
379            method::INDEXER_GET_CAPACITY => {
380                IndexerRequest::GetCapacity(parse_param(data, 1, "lock_hash")?)
381            }
382            method::INDEXER_GET_LIVE_CELLS => {
383                let index_string: String = parse_param(data, 1, "index-type")?;
384                let index = LiveCellIndexType::from_str(index_string.as_str())?;
385                IndexerRequest::LiveCells {
386                    index,
387                    hash: parse_param(data, 2, "hash")?,
388                    from_number: parse_param(data, 3, "from_number")?,
389                    to_number: parse_param(data, 4, "to_number")?,
390                    limit: parse_param(data, 5, "limit")?,
391                }
392            }
393            method::INDEXER_GET_TOPN => IndexerRequest::TopN(parse_param(data, 1, "n")?),
394            method::INDEXER_GET_INDEXER_INFO => IndexerRequest::IndexerInfo,
395            method::INDEXER_ANY => IndexerRequest::Any(parse_param(data, 1, "value")?),
396            _ => {
397                return Err(format!("Invalid request method: {}", data.method));
398            }
399        };
400        Ok(request)
401    }
402}
403
404fn parse_param<T: DeserializeOwned>(
405    data: &JsonrpcRequest,
406    index: usize,
407    field_name: &str,
408) -> Result<T, String> {
409    data.params
410        .get(index)
411        .cloned()
412        .map(|value| {
413            let content: T = serde_json::from_value(value.clone()).map_err(|err| {
414                format!(
415                    "Parse {}'s parameter(field={}, index={}) value: {:?}, failed: {}",
416                    data.method, field_name, index, value, err
417                )
418            })?;
419            Ok(content)
420        })
421        .unwrap_or_else(|| {
422            Err(format!(
423                "Not enough parameter for {}, length: {}, expected: {}",
424                data.method,
425                data.params.len(),
426                index + 1
427            ))
428        })
429}
430
431fn parse_bytes(data: &JsonrpcRequest, index: usize, field: &str) -> Result<Vec<u8>, String> {
432    let hex: String = parse_param(data, index, field)?;
433    if !hex.starts_with("0x") || hex.len() % 2 == 1 {
434        return Err(format!(
435            "Field {} is not valid hex string, method={} (0x prefix is required)",
436            field, data.method
437        ));
438    }
439    let mut dst = vec![0u8; hex.len() / 2 - 1];
440    hex_decode(&hex.as_bytes()[2..], &mut dst).map_err(|err| err.to_string())?;
441    Ok(dst)
442}
443
444fn parse_h256(data: &JsonrpcRequest, index: usize, field: &str) -> Result<[u8; 32], String> {
445    let vec = parse_bytes(data, index, field)?;
446    if vec.len() != 32 {
447        return Err(format!(
448            "Invalid data length for field {}, method={}, expected 32bytes data hex string",
449            field, data.method
450        ));
451    }
452    let mut dst = [0u8; 32];
453    dst.copy_from_slice(&vec);
454    Ok(dst)
455}
456
457impl From<(u64, PluginResponse)> for JsonrpcResponse {
458    fn from((id, response): (u64, PluginResponse)) -> JsonrpcResponse {
459        let (result, error) = match response {
460            PluginResponse::Error(err) => (None, Some(err)),
461            response => (
462                Some(serde_json::to_value(response).expect("Serialize failed")),
463                None,
464            ),
465        };
466        JsonrpcResponse {
467            jsonrpc: JSONRPC_VERSION.to_string(),
468            id: serde_json::json!(id),
469            result,
470            error,
471        }
472    }
473}
474
475impl TryFrom<JsonrpcResponse> for (u64, PluginResponse) {
476    type Error = String;
477    fn try_from(data: JsonrpcResponse) -> Result<(u64, PluginResponse), Self::Error> {
478        if data.jsonrpc != JSONRPC_VERSION {
479            return Err(format!("Invalid jsonrpc field: {}", data.jsonrpc));
480        }
481        let request_id: u64 = serde_json::from_value(data.id)
482            .map_err(|err| format!("Request id must be integer number, error={}", err))?;
483        let response: PluginResponse = if let Some(result) = data.result {
484            serde_json::from_value(result)
485                .map_err(|err| format!("Deserialize response failed, error={}", err))?
486        } else if let Some(error) = data.error {
487            PluginResponse::Error(error)
488        } else {
489            return Err(String::from("Invalid jsonrpc response"));
490        };
491        Ok((request_id, response))
492    }
493}