1use crate::codec::*;
13
14pub const TAG_HANDSHAKE_REQ: u8 = 0;
20pub const TAG_HANDSHAKE_RESP: u8 = 1;
21pub const TAG_FEATURE_SUPPORTED_REQ: u8 = 2;
22pub const TAG_FEATURE_SUPPORTED_RESP: u8 = 3;
23pub const TAG_PUSH_NOTIFICATION_REQ: u8 = 4;
24pub const TAG_PUSH_NOTIFICATION_RESP: u8 = 5;
25pub const TAG_NAVIGATE_TO_REQ: u8 = 6;
26pub const TAG_NAVIGATE_TO_RESP: u8 = 7;
27pub const TAG_DEVICE_PERMISSION_REQ: u8 = 8;
28pub const TAG_DEVICE_PERMISSION_RESP: u8 = 9;
29pub const TAG_REMOTE_PERMISSION_REQ: u8 = 10;
30pub const TAG_REMOTE_PERMISSION_RESP: u8 = 11;
31pub const TAG_LOCAL_STORAGE_READ_REQ: u8 = 12;
32pub const TAG_LOCAL_STORAGE_READ_RESP: u8 = 13;
33pub const TAG_LOCAL_STORAGE_WRITE_REQ: u8 = 14;
34pub const TAG_LOCAL_STORAGE_WRITE_RESP: u8 = 15;
35pub const TAG_LOCAL_STORAGE_CLEAR_REQ: u8 = 16;
36pub const TAG_LOCAL_STORAGE_CLEAR_RESP: u8 = 17;
37pub const TAG_ACCOUNT_STATUS_START: u8 = 18;
39pub const TAG_ACCOUNT_STATUS_STOP: u8 = 19;
40pub const TAG_ACCOUNT_STATUS_INTERRUPT: u8 = 20;
41pub const TAG_ACCOUNT_STATUS_RECEIVE: u8 = 21;
42pub const TAG_ACCOUNT_GET_REQ: u8 = 22;
43pub const TAG_ACCOUNT_GET_RESP: u8 = 23;
44pub const TAG_ACCOUNT_GET_ALIAS_REQ: u8 = 24;
45pub const TAG_ACCOUNT_GET_ALIAS_RESP: u8 = 25;
46pub const TAG_ACCOUNT_CREATE_PROOF_REQ: u8 = 26;
47pub const TAG_ACCOUNT_CREATE_PROOF_RESP: u8 = 27;
48pub const TAG_GET_NON_PRODUCT_ACCOUNTS_REQ: u8 = 28;
49pub const TAG_GET_NON_PRODUCT_ACCOUNTS_RESP: u8 = 29;
50pub const TAG_CREATE_TRANSACTION_REQ: u8 = 30;
51pub const TAG_CREATE_TRANSACTION_RESP: u8 = 31;
52pub const TAG_CREATE_TX_NON_PRODUCT_REQ: u8 = 32;
53pub const TAG_CREATE_TX_NON_PRODUCT_RESP: u8 = 33;
54pub const TAG_SIGN_RAW_REQ: u8 = 34;
55pub const TAG_SIGN_RAW_RESP: u8 = 35;
56pub const TAG_SIGN_PAYLOAD_REQ: u8 = 36;
57pub const TAG_SIGN_PAYLOAD_RESP: u8 = 37;
58pub const TAG_CHAT_CREATE_ROOM_REQ: u8 = 38;
59pub const TAG_CHAT_CREATE_ROOM_RESP: u8 = 39;
60pub const TAG_CHAT_REGISTER_BOT_REQ: u8 = 40;
61pub const TAG_CHAT_REGISTER_BOT_RESP: u8 = 41;
62pub const TAG_CHAT_LIST_START: u8 = 42;
63pub const TAG_CHAT_LIST_STOP: u8 = 43;
64pub const TAG_CHAT_LIST_INTERRUPT: u8 = 44;
65pub const TAG_CHAT_LIST_RECEIVE: u8 = 45;
66pub const TAG_CHAT_POST_MSG_REQ: u8 = 46;
67pub const TAG_CHAT_POST_MSG_RESP: u8 = 47;
68pub const TAG_CHAT_ACTION_START: u8 = 48;
69pub const TAG_CHAT_ACTION_STOP: u8 = 49;
70pub const TAG_CHAT_ACTION_INTERRUPT: u8 = 50;
71pub const TAG_CHAT_ACTION_RECEIVE: u8 = 51;
72pub const TAG_CHAT_CUSTOM_MSG_START: u8 = 52;
73pub const TAG_CHAT_CUSTOM_MSG_STOP: u8 = 53;
74pub const TAG_CHAT_CUSTOM_MSG_INTERRUPT: u8 = 54;
75pub const TAG_CHAT_CUSTOM_MSG_RECEIVE: u8 = 55;
76pub const TAG_STATEMENT_STORE_START: u8 = 56;
77pub const TAG_STATEMENT_STORE_STOP: u8 = 57;
78pub const TAG_STATEMENT_STORE_INTERRUPT: u8 = 58;
79pub const TAG_STATEMENT_STORE_RECEIVE: u8 = 59;
80pub const TAG_STATEMENT_PROOF_REQ: u8 = 60;
81pub const TAG_STATEMENT_PROOF_RESP: u8 = 61;
82pub const TAG_STATEMENT_SUBMIT_REQ: u8 = 62;
83pub const TAG_STATEMENT_SUBMIT_RESP: u8 = 63;
84pub const TAG_PREIMAGE_LOOKUP_START: u8 = 64;
85pub const TAG_PREIMAGE_LOOKUP_STOP: u8 = 65;
86pub const TAG_PREIMAGE_LOOKUP_INTERRUPT: u8 = 66;
87pub const TAG_PREIMAGE_LOOKUP_RECEIVE: u8 = 67;
88pub const TAG_PREIMAGE_SUBMIT_REQ: u8 = 68;
89pub const TAG_PREIMAGE_SUBMIT_RESP: u8 = 69;
90pub const TAG_JSONRPC_SEND_REQ: u8 = 70;
91pub const TAG_JSONRPC_SEND_RESP: u8 = 71;
92pub const TAG_JSONRPC_SUB_START: u8 = 72;
93pub const TAG_JSONRPC_SUB_STOP: u8 = 73;
94pub const TAG_JSONRPC_SUB_INTERRUPT: u8 = 74;
95pub const TAG_JSONRPC_SUB_RECEIVE: u8 = 75;
96pub const TAG_CHAIN_HEAD_FOLLOW_START: u8 = 76;
98pub const TAG_CHAIN_HEAD_FOLLOW_STOP: u8 = 77;
99pub const TAG_CHAIN_HEAD_FOLLOW_INTERRUPT: u8 = 78;
100pub const TAG_CHAIN_HEAD_FOLLOW_RECEIVE: u8 = 79;
101pub const TAG_CHAIN_HEAD_HEADER_REQ: u8 = 80;
103pub const TAG_CHAIN_HEAD_HEADER_RESP: u8 = 81;
104pub const TAG_CHAIN_HEAD_BODY_REQ: u8 = 82;
106pub const TAG_CHAIN_HEAD_BODY_RESP: u8 = 83;
107pub const TAG_CHAIN_HEAD_STORAGE_REQ: u8 = 84;
109pub const TAG_CHAIN_HEAD_STORAGE_RESP: u8 = 85;
110pub const TAG_CHAIN_HEAD_CALL_REQ: u8 = 86;
112pub const TAG_CHAIN_HEAD_CALL_RESP: u8 = 87;
113pub const TAG_CHAIN_HEAD_UNPIN_REQ: u8 = 88;
115pub const TAG_CHAIN_HEAD_UNPIN_RESP: u8 = 89;
116pub const TAG_CHAIN_HEAD_CONTINUE_REQ: u8 = 90;
118pub const TAG_CHAIN_HEAD_CONTINUE_RESP: u8 = 91;
119pub const TAG_CHAIN_HEAD_STOP_OP_REQ: u8 = 92;
121pub const TAG_CHAIN_HEAD_STOP_OP_RESP: u8 = 93;
122pub const TAG_CHAIN_SPEC_GENESIS_REQ: u8 = 94;
124pub const TAG_CHAIN_SPEC_GENESIS_RESP: u8 = 95;
125pub const TAG_CHAIN_SPEC_NAME_REQ: u8 = 96;
127pub const TAG_CHAIN_SPEC_NAME_RESP: u8 = 97;
128pub const TAG_CHAIN_SPEC_PROPS_REQ: u8 = 98;
130pub const TAG_CHAIN_SPEC_PROPS_RESP: u8 = 99;
131pub const TAG_CHAIN_TX_BROADCAST_REQ: u8 = 100;
133pub const TAG_CHAIN_TX_BROADCAST_RESP: u8 = 101;
134pub const TAG_CHAIN_TX_STOP_REQ: u8 = 102;
136pub const TAG_CHAIN_TX_STOP_RESP: u8 = 103;
137
138pub const PROTOCOL_VERSION: u8 = 1;
140
141#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
147pub struct Account {
148 pub public_key: Vec<u8>,
150 pub name: Option<String>,
152}
153
154#[derive(Debug)]
156pub enum HostRequest {
157 Handshake {
158 version: u8,
159 },
160 GetNonProductAccounts,
161 FeatureSupported {
162 feature_data: Vec<u8>,
163 },
164 LocalStorageRead {
165 key: String,
166 },
167 LocalStorageWrite {
168 key: String,
169 value: Vec<u8>,
170 },
171 LocalStorageClear {
172 key: String,
173 },
174 SignPayload {
175 public_key: Vec<u8>,
176 payload: Vec<u8>,
177 },
178 SignRaw {
179 public_key: Vec<u8>,
180 data: Vec<u8>,
181 },
182 CreateTransaction {
183 payload: Vec<u8>,
184 },
185 NavigateTo {
186 url: String,
187 },
188 PushNotification {
189 text: String,
190 deeplink: Option<String>,
191 },
192 AccountConnectionStatusStart,
193 JsonRpcSend {
194 data: Vec<u8>,
195 },
196 JsonRpcSubscribeStart {
197 data: Vec<u8>,
198 },
199 ChainHeadFollowStart {
201 genesis_hash: Vec<u8>,
202 with_runtime: bool,
203 },
204 ChainHeadRequest {
207 tag: u8,
208 genesis_hash: Vec<u8>,
209 follow_sub_id: String,
210 data: serde_json::Value,
211 },
212 ChainSpecRequest {
214 tag: u8,
215 genesis_hash: Vec<u8>,
216 },
217 ChainTxBroadcast {
219 genesis_hash: Vec<u8>,
220 transaction: Vec<u8>,
221 },
222 ChainTxStop {
224 genesis_hash: Vec<u8>,
225 operation_id: String,
226 },
227 Unimplemented {
229 tag: u8,
230 },
231 Unknown {
233 tag: u8,
234 },
235}
236
237#[derive(Debug)]
239pub enum HostResponse {
240 HandshakeOk,
241 AccountList(Vec<Account>),
242 Error(String),
243}
244
245fn bytes_to_hex(bytes: &[u8]) -> String {
251 let mut s = String::with_capacity(2 + bytes.len() * 2);
252 s.push_str("0x");
253 for b in bytes {
254 s.push_str(&format!("{b:02x}"));
255 }
256 s
257}
258
259pub fn decode_message(data: &[u8]) -> Result<(String, u8, HostRequest), DecodeErr> {
261 let mut r = Reader::new(data);
262
263 let request_id = r.read_string()?;
265
266 let tag = r.read_u8()?;
268
269 let req = match tag {
270 TAG_HANDSHAKE_REQ => {
271 let _version_tag = r.read_u8()?; let version = r.read_u8()?;
274 HostRequest::Handshake { version }
275 }
276 TAG_GET_NON_PRODUCT_ACCOUNTS_REQ => {
277 let _version_tag = r.read_u8()?;
279 HostRequest::GetNonProductAccounts
281 }
282 TAG_FEATURE_SUPPORTED_REQ => {
283 let _version_tag = r.read_u8()?;
284 let rest = r.remaining().to_vec();
285 r.skip_rest();
286 HostRequest::FeatureSupported { feature_data: rest }
287 }
288 TAG_LOCAL_STORAGE_READ_REQ => {
289 let _version_tag = r.read_u8()?;
290 let key = r.read_string()?;
291 HostRequest::LocalStorageRead { key }
292 }
293 TAG_LOCAL_STORAGE_WRITE_REQ => {
294 let _version_tag = r.read_u8()?;
295 let key = r.read_string()?;
296 let value = r.read_var_bytes()?;
297 HostRequest::LocalStorageWrite { key, value }
298 }
299 TAG_LOCAL_STORAGE_CLEAR_REQ => {
300 let _version_tag = r.read_u8()?;
301 let key = r.read_string()?;
302 HostRequest::LocalStorageClear { key }
303 }
304 TAG_SIGN_PAYLOAD_REQ => {
305 let _version_tag = r.read_u8()?;
306 let public_key = r.read_var_bytes()?;
307 let payload = r.remaining().to_vec();
308 r.skip_rest();
309 HostRequest::SignPayload {
310 public_key,
311 payload,
312 }
313 }
314 TAG_SIGN_RAW_REQ => {
315 let _version_tag = r.read_u8()?;
316 let public_key = r.read_var_bytes()?;
317 let data = r.remaining().to_vec();
318 r.skip_rest();
319 HostRequest::SignRaw { public_key, data }
320 }
321 TAG_CREATE_TRANSACTION_REQ => {
322 let _version_tag = r.read_u8()?;
323 let rest = r.remaining().to_vec();
324 r.skip_rest();
325 HostRequest::CreateTransaction { payload: rest }
326 }
327 TAG_NAVIGATE_TO_REQ => {
328 let _version_tag = r.read_u8()?;
329 let url = r.read_string()?;
330 HostRequest::NavigateTo { url }
331 }
332 TAG_PUSH_NOTIFICATION_REQ => {
333 let _version_tag = r.read_u8()?;
334 let text = r.read_string()?;
335 let deeplink = r.read_option(|r| r.read_string())?;
336 HostRequest::PushNotification { text, deeplink }
337 }
338 TAG_ACCOUNT_STATUS_START => {
339 let _version_tag = r.read_u8()?;
340 HostRequest::AccountConnectionStatusStart
341 }
342 TAG_JSONRPC_SEND_REQ => {
343 let _version_tag = r.read_u8()?;
344 let rest = r.remaining().to_vec();
345 r.skip_rest();
346 HostRequest::JsonRpcSend { data: rest }
347 }
348 TAG_JSONRPC_SUB_START => {
349 let _version_tag = r.read_u8()?;
350 let rest = r.remaining().to_vec();
351 r.skip_rest();
352 HostRequest::JsonRpcSubscribeStart { data: rest }
353 }
354 TAG_CHAIN_HEAD_FOLLOW_START => {
355 let _version_tag = r.read_u8()?;
356 let genesis_hash = r.read_var_bytes()?;
357 let with_runtime = r.read_u8()? != 0;
358 HostRequest::ChainHeadFollowStart {
359 genesis_hash,
360 with_runtime,
361 }
362 }
363 TAG_CHAIN_HEAD_HEADER_REQ => {
364 let _version_tag = r.read_u8()?;
365 let genesis_hash = r.read_var_bytes()?;
366 let follow_sub_id = r.read_string()?;
367 let hash = r.read_var_bytes()?;
368 let hash_hex = bytes_to_hex(&hash);
369 HostRequest::ChainHeadRequest {
370 tag,
371 genesis_hash,
372 follow_sub_id,
373 data: serde_json::json!([hash_hex]),
374 }
375 }
376 TAG_CHAIN_HEAD_BODY_REQ => {
377 let _version_tag = r.read_u8()?;
378 let genesis_hash = r.read_var_bytes()?;
379 let follow_sub_id = r.read_string()?;
380 let hash = r.read_var_bytes()?;
381 let hash_hex = bytes_to_hex(&hash);
382 HostRequest::ChainHeadRequest {
383 tag,
384 genesis_hash,
385 follow_sub_id,
386 data: serde_json::json!([hash_hex]),
387 }
388 }
389 TAG_CHAIN_HEAD_STORAGE_REQ => {
390 let _version_tag = r.read_u8()?;
391 let genesis_hash = r.read_var_bytes()?;
392 let follow_sub_id = r.read_string()?;
393 let hash = r.read_var_bytes()?;
394 let hash_hex = bytes_to_hex(&hash);
395 let item_count = r.read_compact_u32()?;
397 let mut items = Vec::new();
398 for _ in 0..item_count {
399 let key = r.read_var_bytes()?;
400 let storage_type = r.read_u8()?; let type_str = match storage_type {
402 0 => "value",
403 1 => "hash",
404 2 => "closestDescendantMerkleValue",
405 3 => "descendantsValues",
406 4 => "descendantsHashes",
407 _ => "value",
408 };
409 items.push(serde_json::json!({
410 "key": bytes_to_hex(&key),
411 "type": type_str,
412 }));
413 }
414 let child_trie = r.read_option(|r| r.read_var_bytes())?;
416 let child_trie_hex = child_trie.map(|b| bytes_to_hex(&b));
417 HostRequest::ChainHeadRequest {
418 tag,
419 genesis_hash,
420 follow_sub_id,
421 data: serde_json::json!([hash_hex, items, child_trie_hex]),
422 }
423 }
424 TAG_CHAIN_HEAD_CALL_REQ => {
425 let _version_tag = r.read_u8()?;
426 let genesis_hash = r.read_var_bytes()?;
427 let follow_sub_id = r.read_string()?;
428 let hash = r.read_var_bytes()?;
429 let function = r.read_string()?;
430 let call_params = r.read_var_bytes()?;
431 HostRequest::ChainHeadRequest {
432 tag,
433 genesis_hash,
434 follow_sub_id,
435 data: serde_json::json!([
436 bytes_to_hex(&hash),
437 function,
438 bytes_to_hex(&call_params)
439 ]),
440 }
441 }
442 TAG_CHAIN_HEAD_UNPIN_REQ => {
443 let _version_tag = r.read_u8()?;
444 let genesis_hash = r.read_var_bytes()?;
445 let follow_sub_id = r.read_string()?;
446 let count = r.read_compact_u32()?;
448 let mut hashes = Vec::new();
449 for _ in 0..count {
450 let h = r.read_var_bytes()?;
451 hashes.push(serde_json::Value::String(bytes_to_hex(&h)));
452 }
453 HostRequest::ChainHeadRequest {
454 tag,
455 genesis_hash,
456 follow_sub_id,
457 data: serde_json::json!([hashes]),
458 }
459 }
460 TAG_CHAIN_HEAD_CONTINUE_REQ | TAG_CHAIN_HEAD_STOP_OP_REQ => {
461 let _version_tag = r.read_u8()?;
462 let genesis_hash = r.read_var_bytes()?;
463 let follow_sub_id = r.read_string()?;
464 let operation_id = r.read_string()?;
465 HostRequest::ChainHeadRequest {
466 tag,
467 genesis_hash,
468 follow_sub_id,
469 data: serde_json::json!([operation_id]),
470 }
471 }
472 TAG_CHAIN_SPEC_GENESIS_REQ | TAG_CHAIN_SPEC_NAME_REQ | TAG_CHAIN_SPEC_PROPS_REQ => {
473 let _version_tag = r.read_u8()?;
474 let genesis_hash = r.read_var_bytes()?;
475 HostRequest::ChainSpecRequest { tag, genesis_hash }
476 }
477 TAG_CHAIN_TX_BROADCAST_REQ => {
478 let _version_tag = r.read_u8()?;
479 let genesis_hash = r.read_var_bytes()?;
480 let transaction = r.read_var_bytes()?;
481 HostRequest::ChainTxBroadcast {
482 genesis_hash,
483 transaction,
484 }
485 }
486 TAG_CHAIN_TX_STOP_REQ => {
487 let _version_tag = r.read_u8()?;
488 let genesis_hash = r.read_var_bytes()?;
489 let operation_id = r.read_string()?;
490 HostRequest::ChainTxStop {
491 genesis_hash,
492 operation_id,
493 }
494 }
495 TAG_DEVICE_PERMISSION_REQ
497 | TAG_REMOTE_PERMISSION_REQ
498 | TAG_ACCOUNT_GET_REQ
499 | TAG_ACCOUNT_GET_ALIAS_REQ
500 | TAG_ACCOUNT_CREATE_PROOF_REQ
501 | TAG_CREATE_TX_NON_PRODUCT_REQ
502 | TAG_CHAT_CREATE_ROOM_REQ
503 | TAG_CHAT_REGISTER_BOT_REQ
504 | TAG_CHAT_POST_MSG_REQ
505 | TAG_STATEMENT_PROOF_REQ
506 | TAG_STATEMENT_SUBMIT_REQ
507 | TAG_PREIMAGE_SUBMIT_REQ => HostRequest::Unimplemented { tag },
508 TAG_ACCOUNT_STATUS_STOP
510 | TAG_ACCOUNT_STATUS_INTERRUPT
511 | TAG_CHAT_LIST_STOP
512 | TAG_CHAT_LIST_INTERRUPT
513 | TAG_CHAT_ACTION_STOP
514 | TAG_CHAT_ACTION_INTERRUPT
515 | TAG_CHAT_CUSTOM_MSG_STOP
516 | TAG_CHAT_CUSTOM_MSG_INTERRUPT
517 | TAG_STATEMENT_STORE_STOP
518 | TAG_STATEMENT_STORE_INTERRUPT
519 | TAG_PREIMAGE_LOOKUP_STOP
520 | TAG_PREIMAGE_LOOKUP_INTERRUPT
521 | TAG_JSONRPC_SUB_STOP
522 | TAG_JSONRPC_SUB_INTERRUPT
523 | TAG_CHAIN_HEAD_FOLLOW_STOP
524 | TAG_CHAIN_HEAD_FOLLOW_INTERRUPT => HostRequest::Unimplemented { tag },
525 _ => HostRequest::Unknown { tag },
526 };
527
528 Ok((request_id, tag, req))
529}
530
531pub fn encode_response(request_id: &str, request_tag: u8, response: &HostResponse) -> Vec<u8> {
533 let mut buf = Vec::with_capacity(128);
534
535 encode_string(&mut buf, request_id);
537
538 match response {
539 HostResponse::HandshakeOk => {
540 encode_tag(&mut buf, TAG_HANDSHAKE_RESP);
542 encode_tag(&mut buf, 0);
544 encode_result_ok_void(&mut buf);
546 }
547
548 HostResponse::AccountList(accounts) => {
549 encode_tag(&mut buf, TAG_GET_NON_PRODUCT_ACCOUNTS_RESP);
551 encode_tag(&mut buf, 0);
553 encode_result_ok(&mut buf);
555 encode_vector_len(&mut buf, accounts.len() as u32);
557 for account in accounts {
558 encode_var_bytes(&mut buf, &account.public_key);
561 match &account.name {
563 None => encode_option_none(&mut buf),
564 Some(name) => {
565 encode_option_some(&mut buf);
566 encode_string(&mut buf, name);
567 }
568 }
569 }
570 }
571
572 HostResponse::Error(_reason) => {
573 let resp_tag = response_tag_for(request_tag);
574 encode_tag(&mut buf, resp_tag);
575 encode_tag(&mut buf, 0); encode_result_err(&mut buf);
577 encode_tag(&mut buf, 0);
582 }
583 }
584
585 buf
586}
587
588fn response_tag_for(request_tag: u8) -> u8 {
592 assert!(
593 !matches!(
594 request_tag,
595 TAG_ACCOUNT_STATUS_START
596 | TAG_CHAT_LIST_START
597 | TAG_CHAT_ACTION_START
598 | TAG_CHAT_CUSTOM_MSG_START
599 | TAG_STATEMENT_STORE_START
600 | TAG_PREIMAGE_LOOKUP_START
601 | TAG_JSONRPC_SUB_START
602 | TAG_CHAIN_HEAD_FOLLOW_START
603 ),
604 "response_tag_for called with subscription start tag {request_tag}"
605 );
606 request_tag + 1
607}
608
609pub fn encode_feature_response(request_id: &str, supported: bool) -> Vec<u8> {
611 let mut buf = Vec::with_capacity(32);
612 encode_string(&mut buf, request_id);
613 encode_tag(&mut buf, TAG_FEATURE_SUPPORTED_RESP);
614 encode_tag(&mut buf, 0); encode_result_ok(&mut buf);
616 buf.push(if supported { 1 } else { 0 }); buf
618}
619
620pub fn encode_account_status(request_id: &str, connected: bool) -> Vec<u8> {
622 let mut buf = Vec::with_capacity(32);
623 encode_string(&mut buf, request_id);
624 encode_tag(&mut buf, TAG_ACCOUNT_STATUS_RECEIVE);
625 encode_tag(&mut buf, 0); encode_tag(&mut buf, if connected { 1 } else { 0 });
628 buf
629}
630
631pub fn encode_storage_read_response(request_id: &str, value: Option<&[u8]>) -> Vec<u8> {
633 let mut buf = Vec::with_capacity(64);
634 encode_string(&mut buf, request_id);
635 encode_tag(&mut buf, TAG_LOCAL_STORAGE_READ_RESP);
636 encode_tag(&mut buf, 0); encode_result_ok(&mut buf);
638 match value {
639 None => encode_option_none(&mut buf),
640 Some(v) => {
641 encode_option_some(&mut buf);
642 encode_var_bytes(&mut buf, v);
643 }
644 }
645 buf
646}
647
648pub fn encode_storage_write_response(request_id: &str, is_clear: bool) -> Vec<u8> {
650 let mut buf = Vec::with_capacity(32);
651 encode_string(&mut buf, request_id);
652 let tag = if is_clear {
653 TAG_LOCAL_STORAGE_CLEAR_RESP
654 } else {
655 TAG_LOCAL_STORAGE_WRITE_RESP
656 };
657 encode_tag(&mut buf, tag);
658 encode_tag(&mut buf, 0); encode_result_ok_void(&mut buf);
660 buf
661}
662
663pub fn encode_navigate_response(request_id: &str) -> Vec<u8> {
665 let mut buf = Vec::with_capacity(32);
666 encode_string(&mut buf, request_id);
667 encode_tag(&mut buf, TAG_NAVIGATE_TO_RESP);
668 encode_tag(&mut buf, 0); encode_result_ok_void(&mut buf);
670 buf
671}
672
673pub fn encode_push_notification_response(request_id: &str) -> Vec<u8> {
675 let mut buf = Vec::with_capacity(32);
676 encode_string(&mut buf, request_id);
677 encode_tag(&mut buf, TAG_PUSH_NOTIFICATION_RESP);
678 encode_tag(&mut buf, 0); encode_result_ok_void(&mut buf);
680 buf
681}
682
683pub fn encode_sign_response(request_id: &str, is_raw: bool, signature: &[u8]) -> Vec<u8> {
686 let mut buf = Vec::with_capacity(128);
687 encode_string(&mut buf, request_id);
688 encode_tag(
689 &mut buf,
690 if is_raw {
691 TAG_SIGN_RAW_RESP
692 } else {
693 TAG_SIGN_PAYLOAD_RESP
694 },
695 );
696 encode_tag(&mut buf, 0); encode_result_ok(&mut buf);
698 encode_compact_u32(&mut buf, 0); encode_var_bytes(&mut buf, signature);
700 buf
701}
702
703pub fn encode_jsonrpc_send_response(request_id: &str, json_rpc_result: &str) -> Vec<u8> {
708 let mut buf = Vec::with_capacity(64 + json_rpc_result.len());
709 encode_string(&mut buf, request_id);
710 encode_tag(&mut buf, TAG_JSONRPC_SEND_RESP);
711 encode_tag(&mut buf, 0); encode_result_ok(&mut buf);
713 encode_string(&mut buf, json_rpc_result);
714 buf
715}
716
717pub fn encode_jsonrpc_send_error(request_id: &str) -> Vec<u8> {
719 let mut buf = Vec::with_capacity(32);
720 encode_string(&mut buf, request_id);
721 encode_tag(&mut buf, TAG_JSONRPC_SEND_RESP);
722 encode_tag(&mut buf, 0); encode_result_err(&mut buf);
724 encode_tag(&mut buf, 0); buf
726}
727
728pub fn encode_jsonrpc_sub_receive(request_id: &str, json_rpc_msg: &str) -> Vec<u8> {
734 let mut buf = Vec::with_capacity(64 + json_rpc_msg.len());
735 encode_string(&mut buf, request_id);
736 encode_tag(&mut buf, TAG_JSONRPC_SUB_RECEIVE);
737 encode_tag(&mut buf, 0); encode_string(&mut buf, json_rpc_msg);
739 buf
740}
741
742pub fn encode_sign_error(request_id: &str, is_raw: bool) -> Vec<u8> {
744 let mut buf = Vec::with_capacity(32);
745 encode_string(&mut buf, request_id);
746 encode_tag(
747 &mut buf,
748 if is_raw {
749 TAG_SIGN_RAW_RESP
750 } else {
751 TAG_SIGN_PAYLOAD_RESP
752 },
753 );
754 encode_tag(&mut buf, 0); encode_result_err(&mut buf);
756 encode_tag(&mut buf, 0); buf
758}
759
760#[cfg(test)]
761mod tests {
762 use super::*;
763
764 #[test]
765 fn decode_handshake_request() {
766 let mut msg = Vec::new();
768 encode_string(&mut msg, "test");
769 msg.push(TAG_HANDSHAKE_REQ); msg.push(0); msg.push(PROTOCOL_VERSION); let (id, tag, req) = decode_message(&msg).unwrap();
774 assert_eq!(id, "test");
775 assert_eq!(tag, TAG_HANDSHAKE_REQ);
776 match req {
777 HostRequest::Handshake { version } => assert_eq!(version, 1),
778 _ => panic!("expected Handshake"),
779 }
780 }
781
782 #[test]
783 fn encode_handshake_response() {
784 let resp = encode_response("test", TAG_HANDSHAKE_REQ, &HostResponse::HandshakeOk);
785
786 let mut r = Reader::new(&resp);
788 let id = r.read_string().unwrap();
789 assert_eq!(id, "test");
790 let tag = r.read_u8().unwrap();
791 assert_eq!(tag, TAG_HANDSHAKE_RESP);
792 let v1_tag = r.read_u8().unwrap();
793 assert_eq!(v1_tag, 0);
794 let result_ok = r.read_u8().unwrap();
795 assert_eq!(result_ok, 0x00); assert_eq!(r.pos, resp.len()); }
798
799 #[test]
800 fn decode_get_non_product_accounts() {
801 let mut msg = Vec::new();
802 encode_string(&mut msg, "req-42");
803 msg.push(TAG_GET_NON_PRODUCT_ACCOUNTS_REQ);
804 msg.push(0); let (id, tag, req) = decode_message(&msg).unwrap();
807 assert_eq!(id, "req-42");
808 assert_eq!(tag, TAG_GET_NON_PRODUCT_ACCOUNTS_REQ);
809 assert!(matches!(req, HostRequest::GetNonProductAccounts));
810 }
811
812 #[test]
813 fn encode_account_list_response() {
814 let accounts = vec![
815 Account {
816 public_key: vec![0xd4; 32],
817 name: Some("Alice".into()),
818 },
819 Account {
820 public_key: vec![0x8e; 32],
821 name: None,
822 },
823 ];
824 let resp = encode_response(
825 "req-42",
826 TAG_GET_NON_PRODUCT_ACCOUNTS_REQ,
827 &HostResponse::AccountList(accounts),
828 );
829
830 let mut r = Reader::new(&resp);
832 let id = r.read_string().unwrap();
833 assert_eq!(id, "req-42");
834 let tag = r.read_u8().unwrap();
835 assert_eq!(tag, TAG_GET_NON_PRODUCT_ACCOUNTS_RESP);
836 let v1 = r.read_u8().unwrap();
837 assert_eq!(v1, 0); let result = r.read_u8().unwrap();
839 assert_eq!(result, 0x00); let count = r.read_compact_u32().unwrap();
841 assert_eq!(count, 2);
842
843 let pk1 = r.read_var_bytes().unwrap();
845 assert_eq!(pk1.len(), 32);
846 assert_eq!(pk1[0], 0xd4);
847 let name1 = r.read_option(|r| r.read_string()).unwrap();
848 assert_eq!(name1.as_deref(), Some("Alice"));
849
850 let pk2 = r.read_var_bytes().unwrap();
852 assert_eq!(pk2.len(), 32);
853 assert_eq!(pk2[0], 0x8e);
854 let name2 = r.read_option(|r| r.read_string()).unwrap();
855 assert!(name2.is_none());
856
857 assert_eq!(r.pos, resp.len());
858 }
859
860 #[test]
861 fn handshake_round_trip() {
862 let mut req_msg = Vec::new();
864 encode_string(&mut req_msg, "hsk-1");
865 req_msg.push(TAG_HANDSHAKE_REQ);
866 req_msg.push(0); req_msg.push(PROTOCOL_VERSION);
868
869 let (id, tag, req) = decode_message(&req_msg).unwrap();
870 assert!(matches!(req, HostRequest::Handshake { version: 1 }));
871
872 let resp_bytes = encode_response(&id, tag, &HostResponse::HandshakeOk);
873
874 let mut r = Reader::new(&resp_bytes);
876 assert_eq!(r.read_string().unwrap(), "hsk-1");
877 assert_eq!(r.read_u8().unwrap(), TAG_HANDSHAKE_RESP);
878 assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.read_u8().unwrap(), 0); }
881
882 #[test]
890 fn golden_handshake_request() {
891 let expected: &[u8] = &[
893 0x08, b't', b'1', 0x00, 0x00, 0x01, ];
898 let mut built = Vec::new();
899 encode_string(&mut built, "t1");
900 built.push(TAG_HANDSHAKE_REQ);
901 built.push(0);
902 built.push(PROTOCOL_VERSION);
903 assert_eq!(built, expected);
904 }
905
906 #[test]
907 fn golden_handshake_response_ok() {
908 let resp = encode_response("t1", TAG_HANDSHAKE_REQ, &HostResponse::HandshakeOk);
909 let expected: &[u8] = &[
910 0x08, b't', b'1', 0x01, 0x00, 0x00, ];
915 assert_eq!(resp, expected);
916 }
917
918 #[test]
919 fn golden_get_accounts_request() {
920 let expected: &[u8] = &[
921 0x08, b'a', b'1', 28, 0x00, ];
925 let mut built = Vec::new();
926 encode_string(&mut built, "a1");
927 built.push(TAG_GET_NON_PRODUCT_ACCOUNTS_REQ);
928 built.push(0);
929 assert_eq!(built, expected);
930 }
931
932 #[test]
933 fn golden_get_accounts_response_empty() {
934 let resp = encode_response(
935 "a1",
936 TAG_GET_NON_PRODUCT_ACCOUNTS_REQ,
937 &HostResponse::AccountList(vec![]),
938 );
939 let expected: &[u8] = &[
940 0x08, b'a', b'1', 29, 0x00, 0x00, 0x00, ];
946 assert_eq!(resp, expected);
947 }
948
949 #[test]
950 fn golden_storage_write_response() {
951 let resp = encode_storage_write_response("s1", false);
952 let expected: &[u8] = &[
953 0x08, b's', b'1', 15, 0x00, 0x00, ];
958 assert_eq!(resp, expected);
959 }
960
961 #[test]
962 fn golden_storage_clear_response() {
963 let resp = encode_storage_write_response("s1", true);
964 let expected: &[u8] = &[
965 0x08, b's', b'1', 17, 0x00, 0x00, ];
970 assert_eq!(resp, expected);
971 }
972
973 #[test]
974 fn golden_feature_supported_response() {
975 let resp = encode_feature_response("f1", false);
976 let expected: &[u8] = &[
977 0x08, b'f', b'1', 3, 0x00, 0x00, 0x00, ];
983 assert_eq!(resp, expected);
984 }
985
986 #[test]
987 fn golden_account_status_receive() {
988 let resp = encode_account_status("c1", true);
989 let expected: &[u8] = &[
990 0x08, b'c', b'1', 21, 0x00, 0x01, ];
995 assert_eq!(resp, expected);
996 }
997
998 #[test]
999 fn golden_sign_payload_response_ok() {
1000 let sig = [0xAB; 64];
1001 let resp = encode_sign_response("s1", false, &sig);
1002 let mut r = Reader::new(&resp);
1003 assert_eq!(r.read_string().unwrap(), "s1");
1004 assert_eq!(r.read_u8().unwrap(), TAG_SIGN_PAYLOAD_RESP);
1005 assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.read_compact_u32().unwrap(), 0); let sig_bytes = r.read_var_bytes().unwrap();
1009 assert_eq!(sig_bytes, vec![0xAB; 64]);
1010 assert_eq!(r.pos, resp.len());
1011 }
1012
1013 #[test]
1014 fn golden_sign_raw_response_ok() {
1015 let sig = [0xCD; 64];
1016 let resp = encode_sign_response("s2", true, &sig);
1017 let mut r = Reader::new(&resp);
1018 assert_eq!(r.read_string().unwrap(), "s2");
1019 assert_eq!(r.read_u8().unwrap(), TAG_SIGN_RAW_RESP);
1020 assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.read_compact_u32().unwrap(), 0); let sig_bytes = r.read_var_bytes().unwrap();
1024 assert_eq!(sig_bytes, vec![0xCD; 64]);
1025 }
1026
1027 #[test]
1028 fn golden_sign_error_response() {
1029 let resp = encode_sign_error("s3", false);
1030 let expected: &[u8] = &[
1031 0x08, b's', b'3', 37, 0x00, 0x01, 0x00, ];
1037 assert_eq!(resp, expected);
1038 }
1039
1040 #[test]
1041 fn decode_sign_payload_request() {
1042 let mut msg = Vec::new();
1043 encode_string(&mut msg, "sign-1");
1044 msg.push(TAG_SIGN_PAYLOAD_REQ);
1045 msg.push(0); encode_var_bytes(&mut msg, &[0xAA; 32]); msg.extend_from_slice(b"extrinsic-payload"); let (id, tag, req) = decode_message(&msg).unwrap();
1049 assert_eq!(id, "sign-1");
1050 assert_eq!(tag, TAG_SIGN_PAYLOAD_REQ);
1051 match req {
1052 HostRequest::SignPayload {
1053 public_key,
1054 payload,
1055 } => {
1056 assert_eq!(public_key, vec![0xAA; 32]);
1057 assert_eq!(payload, b"extrinsic-payload");
1058 }
1059 _ => panic!("expected SignPayload"),
1060 }
1061 }
1062
1063 #[test]
1064 fn decode_sign_raw_request() {
1065 let mut msg = Vec::new();
1066 encode_string(&mut msg, "sign-2");
1067 msg.push(TAG_SIGN_RAW_REQ);
1068 msg.push(0); encode_var_bytes(&mut msg, &[0xBB; 32]); msg.extend_from_slice(b"raw-data"); let (id, tag, req) = decode_message(&msg).unwrap();
1072 assert_eq!(id, "sign-2");
1073 assert_eq!(tag, TAG_SIGN_RAW_REQ);
1074 match req {
1075 HostRequest::SignRaw { public_key, data } => {
1076 assert_eq!(public_key, vec![0xBB; 32]);
1077 assert_eq!(data, b"raw-data");
1078 }
1079 _ => panic!("expected SignRaw"),
1080 }
1081 }
1082
1083 #[test]
1086 fn decode_push_notification_request_with_deeplink() {
1087 let mut msg = Vec::new();
1088 encode_string(&mut msg, "pn-1");
1089 msg.push(TAG_PUSH_NOTIFICATION_REQ);
1090 msg.push(0); encode_string(&mut msg, "Transfer complete");
1092 msg.push(0x01); encode_string(&mut msg, "https://app.example/tx/123");
1094
1095 let (id, tag, req) = decode_message(&msg).unwrap();
1096 assert_eq!(id, "pn-1");
1097 assert_eq!(tag, TAG_PUSH_NOTIFICATION_REQ);
1098 match req {
1099 HostRequest::PushNotification { text, deeplink } => {
1100 assert_eq!(text, "Transfer complete");
1101 assert_eq!(deeplink.as_deref(), Some("https://app.example/tx/123"));
1102 }
1103 _ => panic!("expected PushNotification"),
1104 }
1105 }
1106
1107 #[test]
1108 fn decode_push_notification_request_without_deeplink() {
1109 let mut msg = Vec::new();
1110 encode_string(&mut msg, "pn-2");
1111 msg.push(TAG_PUSH_NOTIFICATION_REQ);
1112 msg.push(0); encode_string(&mut msg, "Hello world");
1114 msg.push(0x00); let (id, tag, req) = decode_message(&msg).unwrap();
1117 assert_eq!(id, "pn-2");
1118 assert_eq!(tag, TAG_PUSH_NOTIFICATION_REQ);
1119 match req {
1120 HostRequest::PushNotification { text, deeplink } => {
1121 assert_eq!(text, "Hello world");
1122 assert!(deeplink.is_none());
1123 }
1124 _ => panic!("expected PushNotification"),
1125 }
1126 }
1127
1128 #[test]
1129 fn encode_push_notification_response_produces_ok() {
1130 let resp = encode_push_notification_response("pn-1");
1131 let mut r = Reader::new(&resp);
1132 assert_eq!(r.read_string().unwrap(), "pn-1");
1133 assert_eq!(r.read_u8().unwrap(), TAG_PUSH_NOTIFICATION_RESP);
1134 assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.pos, resp.len());
1137 }
1138
1139 #[test]
1140 fn golden_push_notification_response_ok() {
1141 let resp = encode_push_notification_response("t1");
1142 let expected: &[u8] = &[
1143 0x08, b't', b'1', 0x05, 0x00, 0x00, ];
1148 assert_eq!(resp, expected);
1149 }
1150
1151 #[test]
1152 fn push_notification_round_trip() {
1153 let mut req_msg = Vec::new();
1154 encode_string(&mut req_msg, "pn-rt");
1155 req_msg.push(TAG_PUSH_NOTIFICATION_REQ);
1156 req_msg.push(0); encode_string(&mut req_msg, "Test notification");
1158 req_msg.push(0x00); let (id, _tag, req) = decode_message(&req_msg).unwrap();
1161 assert!(matches!(req, HostRequest::PushNotification { .. }));
1162
1163 let resp_bytes = encode_push_notification_response(&id);
1164
1165 let mut r = Reader::new(&resp_bytes);
1166 assert_eq!(r.read_string().unwrap(), "pn-rt");
1167 assert_eq!(r.read_u8().unwrap(), TAG_PUSH_NOTIFICATION_RESP);
1168 assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.read_u8().unwrap(), 0); }
1171
1172 #[test]
1173 fn golden_storage_read_response_some() {
1174 let resp = encode_storage_read_response("s1", Some(b"hi"));
1175 let expected: &[u8] = &[
1176 0x08, b's', b'1', 13, 0x00, 0x00, 0x01, 0x08, b'h', b'i', ];
1183 assert_eq!(resp, expected);
1184 }
1185
1186 #[test]
1187 fn golden_storage_read_response_none() {
1188 let resp = encode_storage_read_response("s1", None);
1189 let expected: &[u8] = &[
1190 0x08, b's', b'1', 13, 0x00, 0x00, 0x00, ];
1196 assert_eq!(resp, expected);
1197 }
1198 #[test]
1199 fn decode_push_notification_truncated_missing_deeplink() {
1200 let mut msg = Vec::new();
1201 encode_string(&mut msg, "pn-trunc");
1202 msg.push(TAG_PUSH_NOTIFICATION_REQ);
1203 msg.push(0); encode_string(&mut msg, "Hello");
1205 assert!(decode_message(&msg).is_err());
1208 }
1209}