1use tiny_keccak::{Hasher, Keccak};
9
10pub const CONTENTHASH_SELECTOR: [u8; 4] = [0xbc, 0x1c, 0x58, 0xd1];
12
13pub const fn hex_addr(s: &str) -> [u8; 20] {
15 let b = s.as_bytes();
16 assert!(b.len() == 40, "address must be 40 hex chars");
17 let mut out = [0u8; 20];
18 let mut i = 0;
19 while i < 20 {
20 out[i] = (hex_nibble(b[i * 2]) << 4) | hex_nibble(b[i * 2 + 1]);
21 i += 1;
22 }
23 out
24}
25
26pub const fn hex_nibble(c: u8) -> u8 {
28 match c {
29 b'0'..=b'9' => c - b'0',
30 b'a'..=b'f' => c - b'a' + 10,
31 b'A'..=b'F' => c - b'A' + 10,
32 _ => panic!("invalid hex"),
33 }
34}
35
36pub fn keccak256(data: &[u8]) -> [u8; 32] {
38 let mut hasher = Keccak::v256();
39 hasher.update(data);
40 let mut out = [0u8; 32];
41 hasher.finalize(&mut out);
42 out
43}
44
45pub fn namehash(domain: &str) -> [u8; 32] {
50 if domain.is_empty() {
51 return [0u8; 32];
52 }
53 let labels: Vec<&str> = domain.split('.').collect();
54 let mut node = [0u8; 32];
55 for label in labels.into_iter().rev() {
56 let label_hash = keccak256(label.as_bytes());
57 let mut buf = [0u8; 64];
58 buf[..32].copy_from_slice(&node);
59 buf[32..].copy_from_slice(&label_hash);
60 node = keccak256(&buf);
61 }
62 node
63}
64
65pub fn encode_contenthash_call(node: &[u8; 32]) -> Vec<u8> {
67 let mut data = Vec::with_capacity(36);
68 data.extend_from_slice(&CONTENTHASH_SELECTOR);
69 data.extend_from_slice(node);
70 data
71}
72
73pub fn scale_encode_revive_call(dest: &[u8; 20], input_data: &[u8]) -> Result<Vec<u8>, String> {
83 let mut buf = Vec::with_capacity(128 + input_data.len());
84
85 buf.extend_from_slice(&[
87 0xd4, 0x35, 0x93, 0xc7, 0x15, 0xfd, 0xd3, 0x1c, 0x61, 0x14, 0x1a, 0xbd, 0x04, 0xa9, 0x9f,
88 0xd6, 0x82, 0x2c, 0x85, 0x58, 0x85, 0x4c, 0xcd, 0xe3, 0x9a, 0x56, 0x84, 0xe7, 0xa5, 0x6d,
89 0xa2, 0x7d,
90 ]);
91
92 buf.extend_from_slice(dest);
94
95 buf.extend_from_slice(&0u128.to_le_bytes());
97
98 buf.push(0x01); scale_compact_u64(&mut buf, u64::MAX); scale_compact_u64(&mut buf, u64::MAX); buf.push(0x01); buf.extend_from_slice(&(u64::MAX as u128).to_le_bytes());
106
107 scale_compact_len(&mut buf, input_data.len())?;
109 buf.extend_from_slice(input_data);
110
111 Ok(buf)
112}
113
114pub fn scale_compact_u64(buf: &mut Vec<u8>, val: u64) {
116 if val < 64 {
117 buf.push((val as u8) << 2);
118 } else if val < 0x4000 {
119 let v = ((val as u16) << 2) | 1;
120 buf.extend_from_slice(&v.to_le_bytes());
121 } else if val < 0x4000_0000 {
122 let v = ((val as u32) << 2) | 2;
123 buf.extend_from_slice(&v.to_le_bytes());
124 } else {
125 let bytes = val.to_le_bytes();
128 let len = 8 - (val.leading_zeros() / 8) as usize;
129 let len = len.max(4); let prefix = (((len - 4) as u8) << 2) | 3;
131 buf.push(prefix);
132 buf.extend_from_slice(&bytes[..len]);
133 }
134}
135
136pub fn scale_compact_len(buf: &mut Vec<u8>, n: usize) -> Result<(), String> {
141 if n < 64 {
142 buf.push((n as u8) << 2);
143 } else if n < 16384 {
144 let v = ((n as u16) << 2) | 1;
145 buf.extend_from_slice(&v.to_le_bytes());
146 } else if n < 1_073_741_824 {
147 let v = ((n as u32) << 2) | 2;
148 buf.extend_from_slice(&v.to_le_bytes());
149 } else {
150 return Err(format!(
153 "compact encoding: value {n} is too large (max 1_073_741_823)"
154 ));
155 }
156 Ok(())
157}
158
159pub use crate::{hex_decode, hex_encode};
161
162pub fn decode_contract_result(response: &[u8]) -> Result<Vec<u8>, String> {
178 let mut pos = 0;
179
180 let (_, n) = decode_scale_compact(&response[pos..])?;
182 pos += n;
183 let (_, n) = decode_scale_compact(&response[pos..])?;
184 pos += n;
185
186 let (_, n) = decode_scale_compact(&response[pos..])?;
188 pos += n;
189 let (_, n) = decode_scale_compact(&response[pos..])?;
190 pos += n;
191
192 if pos + 17 > response.len() {
194 return Err("response too short (storage_deposit)".into());
195 }
196 pos += 1 + 16;
197
198 if pos >= response.len() {
202 return Err("response too short (extra fields)".into());
203 }
204 let opt_tag = response[pos];
205 pos += 1; if opt_tag == 1 {
207 if pos + 16 > response.len() {
208 return Err("response too short (option balance)".into());
209 }
210 pos += 16; }
212 if pos + 16 > response.len() {
213 return Err("response too short (plain balance)".into());
214 }
215 pos += 16; let (msg_len, bytes_read) = decode_scale_compact(&response[pos..])?;
219 pos += bytes_read + msg_len;
220
221 if pos + 4 > response.len() {
223 return Err("response too short (flags)".into());
224 }
225 pos += 4; let (data_len, bytes_read) = decode_scale_compact(&response[pos..])?;
229 pos += bytes_read;
230
231 if pos + data_len > response.len() {
232 return Err(format!(
233 "data extends beyond response (pos={pos}, data_len={data_len}, total={})",
234 response.len()
235 ));
236 }
237
238 Ok(response[pos..pos + data_len].to_vec())
239}
240
241pub fn decode_scale_compact(data: &[u8]) -> Result<(usize, usize), String> {
243 if data.is_empty() {
244 return Err("empty data for compact decode".into());
245 }
246 let mode = data[0] & 0b11;
247 match mode {
248 0 => Ok(((data[0] >> 2) as usize, 1)),
249 1 => {
250 if data.len() < 2 {
251 return Err("compact: need 2 bytes".into());
252 }
253 let v = u16::from_le_bytes([data[0], data[1]]) >> 2;
254 Ok((v as usize, 2))
255 }
256 2 => {
257 if data.len() < 4 {
258 return Err("compact: need 4 bytes".into());
259 }
260 let v = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) >> 2;
261 Ok((v as usize, 4))
262 }
263 3 => {
264 let bytes_needed = (data[0] >> 2) as usize + 4;
266 if data.len() < 1 + bytes_needed {
267 return Err("compact: big mode insufficient data".into());
268 }
269 let mut val: usize = 0;
270 for i in (0..bytes_needed).rev() {
271 val = (val << 8) | data[1 + i] as usize;
272 }
273 Ok((val, 1 + bytes_needed))
274 }
275 _ => unreachable!(),
276 }
277}
278
279pub fn decode_abi_bytes(data: &[u8]) -> Result<Vec<u8>, String> {
286 if data.len() < 64 {
287 return Err(format!("ABI bytes too short: {} bytes", data.len()));
288 }
289 let len = u32::from_be_bytes([data[60], data[61], data[62], data[63]]) as usize;
292 if 64 + len > data.len() {
293 return Err(format!("ABI bytes: length {len} exceeds data"));
294 }
295 Ok(data[64..64 + len].to_vec())
296}
297
298pub fn contenthash_to_cid(data: &[u8]) -> Result<String, String> {
306 if data.is_empty() {
307 return Err("empty contenthash".into());
308 }
309
310 let (codec, varint_len) = decode_unsigned_varint(data);
314 match codec {
315 0xe3 => {
316 let cid_bytes = &data[varint_len..];
318 Ok(format!("b{}", base32_encode(cid_bytes)))
319 }
320 0xe5 => Err("Swarm contenthash not supported".into()),
321 _ => Err(format!(
322 "contenthash_to_cid: unrecognized codec varint 0x{codec:02x}; only IPFS dag-pb (0x70) is supported"
323 )),
324 }
325}
326
327pub fn decode_unsigned_varint(data: &[u8]) -> (u64, usize) {
329 let mut value: u64 = 0;
330 let mut shift = 0;
331 for (i, &byte) in data.iter().enumerate() {
332 if shift >= 64 {
333 break;
334 }
335 value |= ((byte & 0x7f) as u64) << shift;
336 if byte & 0x80 == 0 {
337 return (value, i + 1);
338 }
339 shift += 7;
340 }
341 (value, data.len())
342}
343
344pub fn base32_encode(data: &[u8]) -> String {
346 const ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz234567";
347 let mut result = String::new();
348 let mut bits: u32 = 0;
349 let mut num_bits: u32 = 0;
350 for &byte in data {
351 bits = (bits << 8) | byte as u32;
352 num_bits += 8;
353 while num_bits >= 5 {
354 num_bits -= 5;
355 result.push(ALPHABET[((bits >> num_bits) & 0x1f) as usize] as char);
356 }
357 }
358 if num_bits > 0 {
359 result.push(ALPHABET[((bits << (5 - num_bits)) & 0x1f) as usize] as char);
360 }
361 result
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn test_hex_nibble_digits() {
370 assert_eq!(hex_nibble(b'0'), 0);
371 assert_eq!(hex_nibble(b'9'), 9);
372 assert_eq!(hex_nibble(b'a'), 10);
373 assert_eq!(hex_nibble(b'f'), 15);
374 assert_eq!(hex_nibble(b'A'), 10);
375 assert_eq!(hex_nibble(b'F'), 15);
376 }
377
378 #[test]
379 fn test_hex_addr_roundtrip() {
380 let addr = hex_addr("7756DF72CBc7f062e7403cD59e45fBc78bed1cD7");
381 assert_eq!(addr[0], 0x77);
382 assert_eq!(addr[19], 0xd7);
383 }
384
385 #[test]
386 fn test_keccak256_empty() {
387 let result = keccak256(b"");
389 assert_eq!(result[0], 0xc5);
390 assert_eq!(result[1], 0xd2);
391 }
392
393 #[test]
394 fn test_namehash_empty_domain() {
395 assert_eq!(namehash(""), [0u8; 32]);
396 }
397
398 #[test]
399 fn test_encode_contenthash_call_length() {
400 let node = [0u8; 32];
401 let call = encode_contenthash_call(&node);
402 assert_eq!(call.len(), 36);
404 assert_eq!(&call[..4], &CONTENTHASH_SELECTOR);
405 }
406
407 #[test]
408 fn test_scale_compact_u64_single_byte() {
409 let mut buf = Vec::new();
410 scale_compact_u64(&mut buf, 0);
411 assert_eq!(buf, vec![0x00]);
412
413 buf.clear();
414 scale_compact_u64(&mut buf, 63);
415 assert_eq!(buf, vec![0xfc]);
416 }
417
418 #[test]
419 fn test_scale_compact_len_too_large_returns_error() {
420 let mut buf = Vec::new();
421 let result = scale_compact_len(&mut buf, 1_073_741_824);
422 assert!(result.is_err());
423 }
424
425 #[test]
426 fn test_hex_encode_decode_roundtrip() {
427 let original = vec![0xde, 0xad, 0xbe, 0xef];
428 let encoded = hex_encode(&original);
429 assert_eq!(encoded, "0xdeadbeef");
430 let decoded = hex_decode(&encoded).unwrap();
431 assert_eq!(decoded, original);
432 }
433
434 #[test]
435 fn test_hex_decode_rejects_odd_length() {
436 assert!(hex_decode("0xabc").is_none());
437 }
438
439 #[test]
440 fn test_decode_scale_compact_single_byte() {
441 let (val, consumed) = decode_scale_compact(&[0x04]).unwrap();
442 assert_eq!(val, 1);
443 assert_eq!(consumed, 1);
444 }
445
446 #[test]
447 fn test_decode_scale_compact_empty_returns_error() {
448 assert!(decode_scale_compact(&[]).is_err());
449 }
450
451 #[test]
452 fn test_decode_abi_bytes_too_short_returns_error() {
453 assert!(decode_abi_bytes(&[0u8; 32]).is_err());
454 }
455
456 #[test]
457 fn test_contenthash_to_cid_empty_returns_error() {
458 assert!(contenthash_to_cid(&[]).is_err());
459 }
460
461 #[test]
462 fn test_contenthash_to_cid_swarm_returns_error() {
463 assert!(contenthash_to_cid(&[0xe5, 0x01, 0x00]).is_err());
465 }
466
467 #[test]
468 fn test_contenthash_to_cid_unknown_codec_returns_error() {
469 let result = contenthash_to_cid(&[0x01, 0x02, 0x03]);
471 assert!(result.is_err());
472 let msg = result.unwrap_err();
473 assert!(msg.contains("unrecognized codec varint"));
474 }
475
476 #[test]
477 fn test_base32_encode_known_vector() {
478 assert_eq!(base32_encode(b""), "");
480 assert_eq!(base32_encode(b"f"), "my");
482 }
483
484 #[test]
485 fn test_decode_unsigned_varint_single_byte() {
486 let (val, consumed) = decode_unsigned_varint(&[0x05]);
487 assert_eq!(val, 5);
488 assert_eq!(consumed, 1);
489 }
490
491 #[test]
492 fn test_decode_unsigned_varint_multi_byte() {
493 let (val, consumed) = decode_unsigned_varint(&[0xac, 0x02]);
495 assert_eq!(val, 300);
496 assert_eq!(consumed, 2);
497 }
498
499 #[test]
502 fn test_namehash_mytestapp_dot_pinned() {
503 let node = namehash("mytestapp.dot");
504 let hex = hex_encode(&node);
507 assert_ne!(node, [0u8; 32]);
510 let expected = hex_decode(&hex).unwrap();
512 assert_eq!(node.to_vec(), expected);
513 assert_eq!(
516 hex,
517 "0xea3cb49a7f22581a2b768fdfd30be01a398514934d65b60e158ee9ee93c20894"
518 );
519 }
520
521 #[test]
524 fn test_scale_encode_revive_call_pinned() {
525 let content_resolver: [u8; 20] = hex_addr("7756DF72CBc7f062e7403cD59e45fBc78bed1cD7");
526 let node = namehash("mytestapp.dot");
527 let call_data = encode_contenthash_call(&node);
528 let params = scale_encode_revive_call(&content_resolver, &call_data).unwrap();
529 let expected = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d7756df72cbc7f062e7403cd59e45fbc78bed1cd7000000000000000000000000000000000113ffffffffffffffff13ffffffffffffffff01ffffffffffffffff000000000000000090bc1c58d1ea3cb49a7f22581a2b768fdfd30be01a398514934d65b60e158ee9ee93c20894";
530 assert_eq!(hex_encode(¶ms), expected);
531 }
532
533 #[test]
535 fn test_contenthash_to_cid_ipfs_pinned() {
536 let digest =
540 hex_decode("0xc3c4733ec8affd06cf9e9ff50ffc6bcd2ec85a6170004bb709669c31de94391a")
541 .unwrap();
542 let mut contenthash = vec![0xe3, 0x01, 0x01, 0x70, 0x12, 0x20];
543 contenthash.extend_from_slice(&digest);
544 let cid = contenthash_to_cid(&contenthash).unwrap();
545 assert!(cid.starts_with("bafybei"));
546 }
547
548 #[test]
553 fn test_scale_compact_u64_two_byte_mode() {
554 let mut buf = Vec::new();
556 scale_compact_u64(&mut buf, 64);
557 assert_eq!(buf, vec![0x01, 0x01]);
559 }
560
561 #[test]
562 fn test_scale_compact_u64_four_byte_mode() {
563 let mut buf = Vec::new();
565 scale_compact_u64(&mut buf, 0x4000);
566 let expected_val: u32 = (0x4000u32 << 2) | 2;
568 assert_eq!(buf, expected_val.to_le_bytes());
569 }
570
571 #[test]
572 fn test_scale_compact_u64_big_mode_u64_max() {
573 let mut buf = Vec::new();
575 scale_compact_u64(&mut buf, u64::MAX);
576 assert_eq!(buf[0], 0x13);
579 assert_eq!(buf.len(), 9);
581 assert_eq!(&buf[1..], &u64::MAX.to_le_bytes());
582 }
583
584 #[test]
589 fn test_scale_compact_len_single_byte_boundary() {
590 let mut buf = Vec::new();
591 scale_compact_len(&mut buf, 0).unwrap();
592 assert_eq!(buf, vec![0x00]);
593
594 buf.clear();
595 scale_compact_len(&mut buf, 63).unwrap();
596 assert_eq!(buf, vec![0xfc]); }
598
599 #[test]
600 fn test_scale_compact_len_two_byte_mode() {
601 let mut buf = Vec::new();
602 scale_compact_len(&mut buf, 64).unwrap();
603 assert_eq!(buf, vec![0x01, 0x01]);
605 }
606
607 #[test]
608 fn test_scale_compact_len_four_byte_mode() {
609 let mut buf = Vec::new();
610 scale_compact_len(&mut buf, 16384).unwrap();
611 let expected: u32 = (16384u32 << 2) | 2;
613 assert_eq!(buf, expected.to_le_bytes());
614 }
615
616 #[test]
621 fn test_decode_scale_compact_two_byte_mode() {
622 let (val, consumed) = decode_scale_compact(&[0x01, 0x01]).unwrap();
624 assert_eq!(val, 64);
625 assert_eq!(consumed, 2);
626 }
627
628 #[test]
629 fn test_decode_scale_compact_two_byte_truncated_returns_error() {
630 assert!(decode_scale_compact(&[0x01]).is_err());
632 }
633
634 #[test]
635 fn test_decode_scale_compact_four_byte_mode() {
636 let v: u32 = (16384u32 << 2) | 2;
638 let input = v.to_le_bytes();
639 let (val, consumed) = decode_scale_compact(&input).unwrap();
640 assert_eq!(val, 16384);
641 assert_eq!(consumed, 4);
642 }
643
644 #[test]
645 fn test_decode_scale_compact_four_byte_truncated_returns_error() {
646 assert!(decode_scale_compact(&[0x02, 0x00, 0x00]).is_err());
648 }
649
650 #[test]
651 fn test_decode_scale_compact_big_mode() {
652 let input = [0x03u8, 0x04, 0x03, 0x02, 0x01];
656 let (val, consumed) = decode_scale_compact(&input).unwrap();
657 assert_eq!(val, 0x01020304);
658 assert_eq!(consumed, 5); }
660
661 #[test]
662 fn test_decode_scale_compact_big_mode_insufficient_data_returns_error() {
663 let input = [0x03u8, 0x04, 0x03, 0x02];
665 assert!(decode_scale_compact(&input).is_err());
666 }
667
668 #[test]
673 fn test_decode_abi_bytes_valid() {
674 let mut data = vec![0u8; 64];
676 data[31] = 0x20;
678 data[63] = 5;
680 data.extend_from_slice(b"hello");
681 let result = decode_abi_bytes(&data).unwrap();
682 assert_eq!(result, b"hello");
683 }
684
685 #[test]
686 fn test_decode_abi_bytes_zero_length() {
687 let mut data = vec![0u8; 64];
689 data[31] = 0x20;
690 let result = decode_abi_bytes(&data).unwrap();
692 assert_eq!(result, b"");
693 }
694
695 #[test]
696 fn test_decode_abi_bytes_length_exceeds_data_returns_error() {
697 let mut data = vec![0u8; 64];
699 data[31] = 0x20;
700 data[63] = 100; assert!(decode_abi_bytes(&data).is_err());
702 }
703
704 #[test]
709 fn test_hex_decode_rejects_invalid_chars() {
710 assert!(hex_decode("0xgg").is_none());
711 assert!(hex_decode("zz").is_none());
712 }
713
714 #[test]
719 fn test_namehash_single_label() {
720 let node = namehash("dot");
722 assert_ne!(node, [0u8; 32]);
723 assert_eq!(node, namehash("dot"));
725 }
726
727 fn build_contract_result(payload: &[u8]) -> Vec<u8> {
735 let mut buf = Vec::new();
736
737 scale_compact_u64(&mut buf, 0); scale_compact_u64(&mut buf, 0); scale_compact_u64(&mut buf, 0);
743 scale_compact_u64(&mut buf, 0);
744
745 buf.push(0x00); buf.extend_from_slice(&0u128.to_le_bytes()); buf.push(0x00); buf.extend_from_slice(&0u128.to_le_bytes()); scale_compact_len(&mut buf, 0).unwrap();
757
758 buf.extend_from_slice(&0u32.to_le_bytes()); scale_compact_len(&mut buf, payload.len()).unwrap();
761 buf.extend_from_slice(payload);
762
763 buf
764 }
765
766 #[test]
767 fn test_decode_contract_result_empty_payload() {
768 let buf = build_contract_result(b"");
769 let result = decode_contract_result(&buf).unwrap();
770 assert_eq!(result, b"");
771 }
772
773 #[test]
774 fn test_decode_contract_result_with_payload() {
775 let payload = b"\x01\x02\x03\x04";
776 let buf = build_contract_result(payload);
777 let result = decode_contract_result(&buf).unwrap();
778 assert_eq!(result, payload);
779 }
780
781 #[test]
782 fn test_decode_contract_result_with_option_some() {
783 let mut buf = Vec::new();
785 scale_compact_u64(&mut buf, 0); scale_compact_u64(&mut buf, 0); scale_compact_u64(&mut buf, 0); scale_compact_u64(&mut buf, 0); buf.push(0x00); buf.extend_from_slice(&0u128.to_le_bytes()); buf.push(0x01); buf.extend_from_slice(&42u128.to_le_bytes()); buf.extend_from_slice(&0u128.to_le_bytes()); scale_compact_len(&mut buf, 0).unwrap(); buf.extend_from_slice(&0u32.to_le_bytes()); scale_compact_len(&mut buf, 3).unwrap(); buf.extend_from_slice(b"abc");
798
799 let result = decode_contract_result(&buf).unwrap();
800 assert_eq!(result, b"abc");
801 }
802
803 #[test]
804 fn test_decode_contract_result_truncated_at_storage_deposit_returns_error() {
805 let mut buf = Vec::new();
807 scale_compact_u64(&mut buf, 0);
808 scale_compact_u64(&mut buf, 0);
809 scale_compact_u64(&mut buf, 0);
810 scale_compact_u64(&mut buf, 0);
811 buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00, 0x00]);
813 assert!(decode_contract_result(&buf).is_err());
814 }
815
816 #[test]
817 fn test_decode_contract_result_truncated_plain_balance_returns_error() {
818 let mut buf = Vec::new();
821 scale_compact_u64(&mut buf, 0);
822 scale_compact_u64(&mut buf, 0);
823 scale_compact_u64(&mut buf, 0);
824 scale_compact_u64(&mut buf, 0);
825 buf.push(0x00); buf.extend_from_slice(&0u128.to_le_bytes()); buf.push(0x00); buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); assert!(decode_contract_result(&buf).is_err());
830 }
831
832 #[test]
833 fn test_decode_contract_result_truncated_option_some_balance_returns_error() {
834 let mut buf = Vec::new();
836 scale_compact_u64(&mut buf, 0);
837 scale_compact_u64(&mut buf, 0);
838 scale_compact_u64(&mut buf, 0);
839 scale_compact_u64(&mut buf, 0);
840 buf.push(0x00); buf.extend_from_slice(&0u128.to_le_bytes()); buf.push(0x01); buf.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); assert!(decode_contract_result(&buf).is_err());
845 }
846
847 #[test]
848 fn test_decode_contract_result_truncated_at_flags_returns_error() {
849 let mut buf = build_contract_result(b"payload");
851 let len = buf.len();
854 buf.truncate(len - 12);
855 assert!(decode_contract_result(&buf).is_err());
858 }
859
860 #[test]
861 fn test_decode_contract_result_data_extends_beyond_response_returns_error() {
862 let mut buf = build_contract_result(b"");
864 let len = buf.len();
867 buf[len - 1] = 100u8 << 2; assert!(decode_contract_result(&buf).is_err());
869 }
870
871 #[test]
872 fn test_decode_contract_result_with_debug_message() {
873 let mut buf = Vec::new();
874 scale_compact_u64(&mut buf, 0);
875 scale_compact_u64(&mut buf, 0);
876 scale_compact_u64(&mut buf, 0);
877 scale_compact_u64(&mut buf, 0);
878 buf.push(0x00);
879 buf.extend_from_slice(&0u128.to_le_bytes());
880 buf.push(0x00); buf.extend_from_slice(&0u128.to_le_bytes());
882 scale_compact_len(&mut buf, 5).unwrap();
884 buf.extend_from_slice(b"debug");
885 buf.extend_from_slice(&0u32.to_le_bytes()); scale_compact_len(&mut buf, 4).unwrap();
887 buf.extend_from_slice(b"data");
888
889 let result = decode_contract_result(&buf).unwrap();
890 assert_eq!(result, b"data");
891 }
892
893 #[test]
898 fn test_scale_encode_revive_call_rejects_oversized_input() {
899 let mut buf = Vec::new();
903 let result = scale_compact_len(&mut buf, 1_073_741_824);
904 assert!(result.is_err());
905 let msg = result.unwrap_err();
906 assert!(msg.contains("too large"));
907 }
908}