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 AccountConnectionStatusStart,
189 JsonRpcSend {
190 data: Vec<u8>,
191 },
192 JsonRpcSubscribeStart {
193 data: Vec<u8>,
194 },
195 ChainHeadFollowStart {
197 genesis_hash: Vec<u8>,
198 with_runtime: bool,
199 },
200 ChainHeadRequest {
203 tag: u8,
204 genesis_hash: Vec<u8>,
205 follow_sub_id: String,
206 data: serde_json::Value,
207 },
208 ChainSpecRequest {
210 tag: u8,
211 genesis_hash: Vec<u8>,
212 },
213 ChainTxBroadcast {
215 genesis_hash: Vec<u8>,
216 transaction: Vec<u8>,
217 },
218 ChainTxStop {
220 genesis_hash: Vec<u8>,
221 operation_id: String,
222 },
223 Unimplemented {
225 tag: u8,
226 },
227 Unknown {
229 tag: u8,
230 },
231}
232
233#[derive(Debug)]
235pub enum HostResponse {
236 HandshakeOk,
237 AccountList(Vec<Account>),
238 Error(String),
239}
240
241fn bytes_to_hex(bytes: &[u8]) -> String {
247 let mut s = String::with_capacity(2 + bytes.len() * 2);
248 s.push_str("0x");
249 for b in bytes {
250 s.push_str(&format!("{b:02x}"));
251 }
252 s
253}
254
255pub fn decode_message(data: &[u8]) -> Result<(String, u8, HostRequest), DecodeErr> {
257 let mut r = Reader::new(data);
258
259 let request_id = r.read_string()?;
261
262 let tag = r.read_u8()?;
264
265 let req = match tag {
266 TAG_HANDSHAKE_REQ => {
267 let _version_tag = r.read_u8()?; let version = r.read_u8()?;
270 HostRequest::Handshake { version }
271 }
272 TAG_GET_NON_PRODUCT_ACCOUNTS_REQ => {
273 let _version_tag = r.read_u8()?;
275 HostRequest::GetNonProductAccounts
277 }
278 TAG_FEATURE_SUPPORTED_REQ => {
279 let _version_tag = r.read_u8()?;
280 let rest = r.remaining().to_vec();
281 r.skip_rest();
282 HostRequest::FeatureSupported { feature_data: rest }
283 }
284 TAG_LOCAL_STORAGE_READ_REQ => {
285 let _version_tag = r.read_u8()?;
286 let key = r.read_string()?;
287 HostRequest::LocalStorageRead { key }
288 }
289 TAG_LOCAL_STORAGE_WRITE_REQ => {
290 let _version_tag = r.read_u8()?;
291 let key = r.read_string()?;
292 let value = r.read_var_bytes()?;
293 HostRequest::LocalStorageWrite { key, value }
294 }
295 TAG_LOCAL_STORAGE_CLEAR_REQ => {
296 let _version_tag = r.read_u8()?;
297 let key = r.read_string()?;
298 HostRequest::LocalStorageClear { key }
299 }
300 TAG_SIGN_PAYLOAD_REQ => {
301 let _version_tag = r.read_u8()?;
302 let public_key = r.read_var_bytes()?;
303 let payload = r.remaining().to_vec();
304 r.skip_rest();
305 HostRequest::SignPayload {
306 public_key,
307 payload,
308 }
309 }
310 TAG_SIGN_RAW_REQ => {
311 let _version_tag = r.read_u8()?;
312 let public_key = r.read_var_bytes()?;
313 let data = r.remaining().to_vec();
314 r.skip_rest();
315 HostRequest::SignRaw { public_key, data }
316 }
317 TAG_CREATE_TRANSACTION_REQ => {
318 let _version_tag = r.read_u8()?;
319 let rest = r.remaining().to_vec();
320 r.skip_rest();
321 HostRequest::CreateTransaction { payload: rest }
322 }
323 TAG_NAVIGATE_TO_REQ => {
324 let _version_tag = r.read_u8()?;
325 let url = r.read_string()?;
326 HostRequest::NavigateTo { url }
327 }
328 TAG_ACCOUNT_STATUS_START => {
329 let _version_tag = r.read_u8()?;
330 HostRequest::AccountConnectionStatusStart
331 }
332 TAG_JSONRPC_SEND_REQ => {
333 let _version_tag = r.read_u8()?;
334 let rest = r.remaining().to_vec();
335 r.skip_rest();
336 HostRequest::JsonRpcSend { data: rest }
337 }
338 TAG_JSONRPC_SUB_START => {
339 let _version_tag = r.read_u8()?;
340 let rest = r.remaining().to_vec();
341 r.skip_rest();
342 HostRequest::JsonRpcSubscribeStart { data: rest }
343 }
344 TAG_CHAIN_HEAD_FOLLOW_START => {
345 let _version_tag = r.read_u8()?;
346 let genesis_hash = r.read_var_bytes()?;
347 let with_runtime = r.read_u8()? != 0;
348 HostRequest::ChainHeadFollowStart {
349 genesis_hash,
350 with_runtime,
351 }
352 }
353 TAG_CHAIN_HEAD_HEADER_REQ => {
354 let _version_tag = r.read_u8()?;
355 let genesis_hash = r.read_var_bytes()?;
356 let follow_sub_id = r.read_string()?;
357 let hash = r.read_var_bytes()?;
358 let hash_hex = bytes_to_hex(&hash);
359 HostRequest::ChainHeadRequest {
360 tag,
361 genesis_hash,
362 follow_sub_id,
363 data: serde_json::json!([hash_hex]),
364 }
365 }
366 TAG_CHAIN_HEAD_BODY_REQ => {
367 let _version_tag = r.read_u8()?;
368 let genesis_hash = r.read_var_bytes()?;
369 let follow_sub_id = r.read_string()?;
370 let hash = r.read_var_bytes()?;
371 let hash_hex = bytes_to_hex(&hash);
372 HostRequest::ChainHeadRequest {
373 tag,
374 genesis_hash,
375 follow_sub_id,
376 data: serde_json::json!([hash_hex]),
377 }
378 }
379 TAG_CHAIN_HEAD_STORAGE_REQ => {
380 let _version_tag = r.read_u8()?;
381 let genesis_hash = r.read_var_bytes()?;
382 let follow_sub_id = r.read_string()?;
383 let hash = r.read_var_bytes()?;
384 let hash_hex = bytes_to_hex(&hash);
385 let item_count = r.read_compact_u32()?;
387 let mut items = Vec::new();
388 for _ in 0..item_count {
389 let key = r.read_var_bytes()?;
390 let storage_type = r.read_u8()?; let type_str = match storage_type {
392 0 => "value",
393 1 => "hash",
394 2 => "closestDescendantMerkleValue",
395 3 => "descendantsValues",
396 4 => "descendantsHashes",
397 _ => "value",
398 };
399 items.push(serde_json::json!({
400 "key": bytes_to_hex(&key),
401 "type": type_str,
402 }));
403 }
404 let child_trie = r.read_option(|r| r.read_var_bytes())?;
406 let child_trie_hex = child_trie.map(|b| bytes_to_hex(&b));
407 HostRequest::ChainHeadRequest {
408 tag,
409 genesis_hash,
410 follow_sub_id,
411 data: serde_json::json!([hash_hex, items, child_trie_hex]),
412 }
413 }
414 TAG_CHAIN_HEAD_CALL_REQ => {
415 let _version_tag = r.read_u8()?;
416 let genesis_hash = r.read_var_bytes()?;
417 let follow_sub_id = r.read_string()?;
418 let hash = r.read_var_bytes()?;
419 let function = r.read_string()?;
420 let call_params = r.read_var_bytes()?;
421 HostRequest::ChainHeadRequest {
422 tag,
423 genesis_hash,
424 follow_sub_id,
425 data: serde_json::json!([
426 bytes_to_hex(&hash),
427 function,
428 bytes_to_hex(&call_params)
429 ]),
430 }
431 }
432 TAG_CHAIN_HEAD_UNPIN_REQ => {
433 let _version_tag = r.read_u8()?;
434 let genesis_hash = r.read_var_bytes()?;
435 let follow_sub_id = r.read_string()?;
436 let count = r.read_compact_u32()?;
438 let mut hashes = Vec::new();
439 for _ in 0..count {
440 let h = r.read_var_bytes()?;
441 hashes.push(serde_json::Value::String(bytes_to_hex(&h)));
442 }
443 HostRequest::ChainHeadRequest {
444 tag,
445 genesis_hash,
446 follow_sub_id,
447 data: serde_json::json!([hashes]),
448 }
449 }
450 TAG_CHAIN_HEAD_CONTINUE_REQ | TAG_CHAIN_HEAD_STOP_OP_REQ => {
451 let _version_tag = r.read_u8()?;
452 let genesis_hash = r.read_var_bytes()?;
453 let follow_sub_id = r.read_string()?;
454 let operation_id = r.read_string()?;
455 HostRequest::ChainHeadRequest {
456 tag,
457 genesis_hash,
458 follow_sub_id,
459 data: serde_json::json!([operation_id]),
460 }
461 }
462 TAG_CHAIN_SPEC_GENESIS_REQ | TAG_CHAIN_SPEC_NAME_REQ | TAG_CHAIN_SPEC_PROPS_REQ => {
463 let _version_tag = r.read_u8()?;
464 let genesis_hash = r.read_var_bytes()?;
465 HostRequest::ChainSpecRequest { tag, genesis_hash }
466 }
467 TAG_CHAIN_TX_BROADCAST_REQ => {
468 let _version_tag = r.read_u8()?;
469 let genesis_hash = r.read_var_bytes()?;
470 let transaction = r.read_var_bytes()?;
471 HostRequest::ChainTxBroadcast {
472 genesis_hash,
473 transaction,
474 }
475 }
476 TAG_CHAIN_TX_STOP_REQ => {
477 let _version_tag = r.read_u8()?;
478 let genesis_hash = r.read_var_bytes()?;
479 let operation_id = r.read_string()?;
480 HostRequest::ChainTxStop {
481 genesis_hash,
482 operation_id,
483 }
484 }
485 TAG_PUSH_NOTIFICATION_REQ
487 | TAG_DEVICE_PERMISSION_REQ
488 | TAG_REMOTE_PERMISSION_REQ
489 | TAG_ACCOUNT_GET_REQ
490 | TAG_ACCOUNT_GET_ALIAS_REQ
491 | TAG_ACCOUNT_CREATE_PROOF_REQ
492 | TAG_CREATE_TX_NON_PRODUCT_REQ
493 | TAG_CHAT_CREATE_ROOM_REQ
494 | TAG_CHAT_REGISTER_BOT_REQ
495 | TAG_CHAT_POST_MSG_REQ
496 | TAG_STATEMENT_PROOF_REQ
497 | TAG_STATEMENT_SUBMIT_REQ
498 | TAG_PREIMAGE_SUBMIT_REQ => HostRequest::Unimplemented { tag },
499 TAG_ACCOUNT_STATUS_STOP
501 | TAG_ACCOUNT_STATUS_INTERRUPT
502 | TAG_CHAT_LIST_STOP
503 | TAG_CHAT_LIST_INTERRUPT
504 | TAG_CHAT_ACTION_STOP
505 | TAG_CHAT_ACTION_INTERRUPT
506 | TAG_CHAT_CUSTOM_MSG_STOP
507 | TAG_CHAT_CUSTOM_MSG_INTERRUPT
508 | TAG_STATEMENT_STORE_STOP
509 | TAG_STATEMENT_STORE_INTERRUPT
510 | TAG_PREIMAGE_LOOKUP_STOP
511 | TAG_PREIMAGE_LOOKUP_INTERRUPT
512 | TAG_JSONRPC_SUB_STOP
513 | TAG_JSONRPC_SUB_INTERRUPT
514 | TAG_CHAIN_HEAD_FOLLOW_STOP
515 | TAG_CHAIN_HEAD_FOLLOW_INTERRUPT => HostRequest::Unimplemented { tag },
516 _ => HostRequest::Unknown { tag },
517 };
518
519 Ok((request_id, tag, req))
520}
521
522pub fn encode_response(request_id: &str, request_tag: u8, response: &HostResponse) -> Vec<u8> {
524 let mut buf = Vec::with_capacity(128);
525
526 encode_string(&mut buf, request_id);
528
529 match response {
530 HostResponse::HandshakeOk => {
531 encode_tag(&mut buf, TAG_HANDSHAKE_RESP);
533 encode_tag(&mut buf, 0);
535 encode_result_ok_void(&mut buf);
537 }
538
539 HostResponse::AccountList(accounts) => {
540 encode_tag(&mut buf, TAG_GET_NON_PRODUCT_ACCOUNTS_RESP);
542 encode_tag(&mut buf, 0);
544 encode_result_ok(&mut buf);
546 encode_vector_len(&mut buf, accounts.len() as u32);
548 for account in accounts {
549 encode_var_bytes(&mut buf, &account.public_key);
552 match &account.name {
554 None => encode_option_none(&mut buf),
555 Some(name) => {
556 encode_option_some(&mut buf);
557 encode_string(&mut buf, name);
558 }
559 }
560 }
561 }
562
563 HostResponse::Error(_reason) => {
564 let resp_tag = response_tag_for(request_tag);
565 encode_tag(&mut buf, resp_tag);
566 encode_tag(&mut buf, 0); encode_result_err(&mut buf);
568 encode_tag(&mut buf, 0);
573 }
574 }
575
576 buf
577}
578
579fn response_tag_for(request_tag: u8) -> u8 {
583 assert!(
584 !matches!(
585 request_tag,
586 TAG_ACCOUNT_STATUS_START
587 | TAG_CHAT_LIST_START
588 | TAG_CHAT_ACTION_START
589 | TAG_CHAT_CUSTOM_MSG_START
590 | TAG_STATEMENT_STORE_START
591 | TAG_PREIMAGE_LOOKUP_START
592 | TAG_JSONRPC_SUB_START
593 | TAG_CHAIN_HEAD_FOLLOW_START
594 ),
595 "response_tag_for called with subscription start tag {request_tag}"
596 );
597 request_tag + 1
598}
599
600pub fn encode_feature_response(request_id: &str, supported: bool) -> Vec<u8> {
602 let mut buf = Vec::with_capacity(32);
603 encode_string(&mut buf, request_id);
604 encode_tag(&mut buf, TAG_FEATURE_SUPPORTED_RESP);
605 encode_tag(&mut buf, 0); encode_result_ok(&mut buf);
607 buf.push(if supported { 1 } else { 0 }); buf
609}
610
611pub fn encode_account_status(request_id: &str, connected: bool) -> Vec<u8> {
613 let mut buf = Vec::with_capacity(32);
614 encode_string(&mut buf, request_id);
615 encode_tag(&mut buf, TAG_ACCOUNT_STATUS_RECEIVE);
616 encode_tag(&mut buf, 0); encode_tag(&mut buf, if connected { 1 } else { 0 });
619 buf
620}
621
622pub fn encode_storage_read_response(request_id: &str, value: Option<&[u8]>) -> Vec<u8> {
624 let mut buf = Vec::with_capacity(64);
625 encode_string(&mut buf, request_id);
626 encode_tag(&mut buf, TAG_LOCAL_STORAGE_READ_RESP);
627 encode_tag(&mut buf, 0); encode_result_ok(&mut buf);
629 match value {
630 None => encode_option_none(&mut buf),
631 Some(v) => {
632 encode_option_some(&mut buf);
633 encode_var_bytes(&mut buf, v);
634 }
635 }
636 buf
637}
638
639pub fn encode_storage_write_response(request_id: &str, is_clear: bool) -> Vec<u8> {
641 let mut buf = Vec::with_capacity(32);
642 encode_string(&mut buf, request_id);
643 let tag = if is_clear {
644 TAG_LOCAL_STORAGE_CLEAR_RESP
645 } else {
646 TAG_LOCAL_STORAGE_WRITE_RESP
647 };
648 encode_tag(&mut buf, tag);
649 encode_tag(&mut buf, 0); encode_result_ok_void(&mut buf);
651 buf
652}
653
654pub fn encode_navigate_response(request_id: &str) -> Vec<u8> {
656 let mut buf = Vec::with_capacity(32);
657 encode_string(&mut buf, request_id);
658 encode_tag(&mut buf, TAG_NAVIGATE_TO_RESP);
659 encode_tag(&mut buf, 0); encode_result_ok_void(&mut buf);
661 buf
662}
663
664pub fn encode_sign_response(request_id: &str, is_raw: bool, signature: &[u8]) -> Vec<u8> {
667 let mut buf = Vec::with_capacity(128);
668 encode_string(&mut buf, request_id);
669 encode_tag(
670 &mut buf,
671 if is_raw {
672 TAG_SIGN_RAW_RESP
673 } else {
674 TAG_SIGN_PAYLOAD_RESP
675 },
676 );
677 encode_tag(&mut buf, 0); encode_result_ok(&mut buf);
679 encode_compact_u32(&mut buf, 0); encode_var_bytes(&mut buf, signature);
681 buf
682}
683
684pub fn encode_jsonrpc_send_response(request_id: &str, json_rpc_result: &str) -> Vec<u8> {
689 let mut buf = Vec::with_capacity(64 + json_rpc_result.len());
690 encode_string(&mut buf, request_id);
691 encode_tag(&mut buf, TAG_JSONRPC_SEND_RESP);
692 encode_tag(&mut buf, 0); encode_result_ok(&mut buf);
694 encode_string(&mut buf, json_rpc_result);
695 buf
696}
697
698pub fn encode_jsonrpc_send_error(request_id: &str) -> Vec<u8> {
700 let mut buf = Vec::with_capacity(32);
701 encode_string(&mut buf, request_id);
702 encode_tag(&mut buf, TAG_JSONRPC_SEND_RESP);
703 encode_tag(&mut buf, 0); encode_result_err(&mut buf);
705 encode_tag(&mut buf, 0); buf
707}
708
709pub fn encode_jsonrpc_sub_receive(request_id: &str, json_rpc_msg: &str) -> Vec<u8> {
715 let mut buf = Vec::with_capacity(64 + json_rpc_msg.len());
716 encode_string(&mut buf, request_id);
717 encode_tag(&mut buf, TAG_JSONRPC_SUB_RECEIVE);
718 encode_tag(&mut buf, 0); encode_string(&mut buf, json_rpc_msg);
720 buf
721}
722
723pub fn encode_sign_error(request_id: &str, is_raw: bool) -> Vec<u8> {
725 let mut buf = Vec::with_capacity(32);
726 encode_string(&mut buf, request_id);
727 encode_tag(
728 &mut buf,
729 if is_raw {
730 TAG_SIGN_RAW_RESP
731 } else {
732 TAG_SIGN_PAYLOAD_RESP
733 },
734 );
735 encode_tag(&mut buf, 0); encode_result_err(&mut buf);
737 encode_tag(&mut buf, 0); buf
739}
740
741#[cfg(test)]
742mod tests {
743 use super::*;
744
745 #[test]
746 fn decode_handshake_request() {
747 let mut msg = Vec::new();
749 encode_string(&mut msg, "test");
750 msg.push(TAG_HANDSHAKE_REQ); msg.push(0); msg.push(PROTOCOL_VERSION); let (id, tag, req) = decode_message(&msg).unwrap();
755 assert_eq!(id, "test");
756 assert_eq!(tag, TAG_HANDSHAKE_REQ);
757 match req {
758 HostRequest::Handshake { version } => assert_eq!(version, 1),
759 _ => panic!("expected Handshake"),
760 }
761 }
762
763 #[test]
764 fn encode_handshake_response() {
765 let resp = encode_response("test", TAG_HANDSHAKE_REQ, &HostResponse::HandshakeOk);
766
767 let mut r = Reader::new(&resp);
769 let id = r.read_string().unwrap();
770 assert_eq!(id, "test");
771 let tag = r.read_u8().unwrap();
772 assert_eq!(tag, TAG_HANDSHAKE_RESP);
773 let v1_tag = r.read_u8().unwrap();
774 assert_eq!(v1_tag, 0);
775 let result_ok = r.read_u8().unwrap();
776 assert_eq!(result_ok, 0x00); assert_eq!(r.pos, resp.len()); }
779
780 #[test]
781 fn decode_get_non_product_accounts() {
782 let mut msg = Vec::new();
783 encode_string(&mut msg, "req-42");
784 msg.push(TAG_GET_NON_PRODUCT_ACCOUNTS_REQ);
785 msg.push(0); let (id, tag, req) = decode_message(&msg).unwrap();
788 assert_eq!(id, "req-42");
789 assert_eq!(tag, TAG_GET_NON_PRODUCT_ACCOUNTS_REQ);
790 assert!(matches!(req, HostRequest::GetNonProductAccounts));
791 }
792
793 #[test]
794 fn encode_account_list_response() {
795 let accounts = vec![
796 Account {
797 public_key: vec![0xd4; 32],
798 name: Some("Alice".into()),
799 },
800 Account {
801 public_key: vec![0x8e; 32],
802 name: None,
803 },
804 ];
805 let resp = encode_response(
806 "req-42",
807 TAG_GET_NON_PRODUCT_ACCOUNTS_REQ,
808 &HostResponse::AccountList(accounts),
809 );
810
811 let mut r = Reader::new(&resp);
813 let id = r.read_string().unwrap();
814 assert_eq!(id, "req-42");
815 let tag = r.read_u8().unwrap();
816 assert_eq!(tag, TAG_GET_NON_PRODUCT_ACCOUNTS_RESP);
817 let v1 = r.read_u8().unwrap();
818 assert_eq!(v1, 0); let result = r.read_u8().unwrap();
820 assert_eq!(result, 0x00); let count = r.read_compact_u32().unwrap();
822 assert_eq!(count, 2);
823
824 let pk1 = r.read_var_bytes().unwrap();
826 assert_eq!(pk1.len(), 32);
827 assert_eq!(pk1[0], 0xd4);
828 let name1 = r.read_option(|r| r.read_string()).unwrap();
829 assert_eq!(name1.as_deref(), Some("Alice"));
830
831 let pk2 = r.read_var_bytes().unwrap();
833 assert_eq!(pk2.len(), 32);
834 assert_eq!(pk2[0], 0x8e);
835 let name2 = r.read_option(|r| r.read_string()).unwrap();
836 assert!(name2.is_none());
837
838 assert_eq!(r.pos, resp.len());
839 }
840
841 #[test]
842 fn handshake_round_trip() {
843 let mut req_msg = Vec::new();
845 encode_string(&mut req_msg, "hsk-1");
846 req_msg.push(TAG_HANDSHAKE_REQ);
847 req_msg.push(0); req_msg.push(PROTOCOL_VERSION);
849
850 let (id, tag, req) = decode_message(&req_msg).unwrap();
851 assert!(matches!(req, HostRequest::Handshake { version: 1 }));
852
853 let resp_bytes = encode_response(&id, tag, &HostResponse::HandshakeOk);
854
855 let mut r = Reader::new(&resp_bytes);
857 assert_eq!(r.read_string().unwrap(), "hsk-1");
858 assert_eq!(r.read_u8().unwrap(), TAG_HANDSHAKE_RESP);
859 assert_eq!(r.read_u8().unwrap(), 0); assert_eq!(r.read_u8().unwrap(), 0); }
862
863 #[test]
871 fn golden_handshake_request() {
872 let expected: &[u8] = &[
874 0x08, b't', b'1', 0x00, 0x00, 0x01, ];
879 let mut built = Vec::new();
880 encode_string(&mut built, "t1");
881 built.push(TAG_HANDSHAKE_REQ);
882 built.push(0);
883 built.push(PROTOCOL_VERSION);
884 assert_eq!(built, expected);
885 }
886
887 #[test]
888 fn golden_handshake_response_ok() {
889 let resp = encode_response("t1", TAG_HANDSHAKE_REQ, &HostResponse::HandshakeOk);
890 let expected: &[u8] = &[
891 0x08, b't', b'1', 0x01, 0x00, 0x00, ];
896 assert_eq!(resp, expected);
897 }
898
899 #[test]
900 fn golden_get_accounts_request() {
901 let expected: &[u8] = &[
902 0x08, b'a', b'1', 28, 0x00, ];
906 let mut built = Vec::new();
907 encode_string(&mut built, "a1");
908 built.push(TAG_GET_NON_PRODUCT_ACCOUNTS_REQ);
909 built.push(0);
910 assert_eq!(built, expected);
911 }
912
913 #[test]
914 fn golden_get_accounts_response_empty() {
915 let resp = encode_response(
916 "a1",
917 TAG_GET_NON_PRODUCT_ACCOUNTS_REQ,
918 &HostResponse::AccountList(vec![]),
919 );
920 let expected: &[u8] = &[
921 0x08, b'a', b'1', 29, 0x00, 0x00, 0x00, ];
927 assert_eq!(resp, expected);
928 }
929
930 #[test]
931 fn golden_storage_write_response() {
932 let resp = encode_storage_write_response("s1", false);
933 let expected: &[u8] = &[
934 0x08, b's', b'1', 15, 0x00, 0x00, ];
939 assert_eq!(resp, expected);
940 }
941
942 #[test]
943 fn golden_storage_clear_response() {
944 let resp = encode_storage_write_response("s1", true);
945 let expected: &[u8] = &[
946 0x08, b's', b'1', 17, 0x00, 0x00, ];
951 assert_eq!(resp, expected);
952 }
953
954 #[test]
955 fn golden_feature_supported_response() {
956 let resp = encode_feature_response("f1", false);
957 let expected: &[u8] = &[
958 0x08, b'f', b'1', 3, 0x00, 0x00, 0x00, ];
964 assert_eq!(resp, expected);
965 }
966
967 #[test]
968 fn golden_account_status_receive() {
969 let resp = encode_account_status("c1", true);
970 let expected: &[u8] = &[
971 0x08, b'c', b'1', 21, 0x00, 0x01, ];
976 assert_eq!(resp, expected);
977 }
978
979 #[test]
980 fn golden_sign_payload_response_ok() {
981 let sig = [0xAB; 64];
982 let resp = encode_sign_response("s1", false, &sig);
983 let mut r = Reader::new(&resp);
984 assert_eq!(r.read_string().unwrap(), "s1");
985 assert_eq!(r.read_u8().unwrap(), TAG_SIGN_PAYLOAD_RESP);
986 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();
990 assert_eq!(sig_bytes, vec![0xAB; 64]);
991 assert_eq!(r.pos, resp.len());
992 }
993
994 #[test]
995 fn golden_sign_raw_response_ok() {
996 let sig = [0xCD; 64];
997 let resp = encode_sign_response("s2", true, &sig);
998 let mut r = Reader::new(&resp);
999 assert_eq!(r.read_string().unwrap(), "s2");
1000 assert_eq!(r.read_u8().unwrap(), TAG_SIGN_RAW_RESP);
1001 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();
1005 assert_eq!(sig_bytes, vec![0xCD; 64]);
1006 }
1007
1008 #[test]
1009 fn golden_sign_error_response() {
1010 let resp = encode_sign_error("s3", false);
1011 let expected: &[u8] = &[
1012 0x08, b's', b'3', 37, 0x00, 0x01, 0x00, ];
1018 assert_eq!(resp, expected);
1019 }
1020
1021 #[test]
1022 fn decode_sign_payload_request() {
1023 let mut msg = Vec::new();
1024 encode_string(&mut msg, "sign-1");
1025 msg.push(TAG_SIGN_PAYLOAD_REQ);
1026 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();
1030 assert_eq!(id, "sign-1");
1031 assert_eq!(tag, TAG_SIGN_PAYLOAD_REQ);
1032 match req {
1033 HostRequest::SignPayload {
1034 public_key,
1035 payload,
1036 } => {
1037 assert_eq!(public_key, vec![0xAA; 32]);
1038 assert_eq!(payload, b"extrinsic-payload");
1039 }
1040 _ => panic!("expected SignPayload"),
1041 }
1042 }
1043
1044 #[test]
1045 fn decode_sign_raw_request() {
1046 let mut msg = Vec::new();
1047 encode_string(&mut msg, "sign-2");
1048 msg.push(TAG_SIGN_RAW_REQ);
1049 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();
1053 assert_eq!(id, "sign-2");
1054 assert_eq!(tag, TAG_SIGN_RAW_REQ);
1055 match req {
1056 HostRequest::SignRaw { public_key, data } => {
1057 assert_eq!(public_key, vec![0xBB; 32]);
1058 assert_eq!(data, b"raw-data");
1059 }
1060 _ => panic!("expected SignRaw"),
1061 }
1062 }
1063
1064 #[test]
1065 fn golden_storage_read_response_some() {
1066 let resp = encode_storage_read_response("s1", Some(b"hi"));
1067 let expected: &[u8] = &[
1068 0x08, b's', b'1', 13, 0x00, 0x00, 0x01, 0x08, b'h', b'i', ];
1075 assert_eq!(resp, expected);
1076 }
1077
1078 #[test]
1079 fn golden_storage_read_response_none() {
1080 let resp = encode_storage_read_response("s1", None);
1081 let expected: &[u8] = &[
1082 0x08, b's', b'1', 13, 0x00, 0x00, 0x00, ];
1088 assert_eq!(resp, expected);
1089 }
1090}