zebra_chain/transaction/
memo.rs

1use std::{cmp, fmt};
2
3/// A 512-byte (plaintext) memo associated with a note, as described in
4/// [protocol specification §5.5][ps].
5///
6/// The _Memo_ field of a note is a plaintext type; the parent note is
7/// what is encrypted and stored on the blockchain. The underlying
8/// usage of the memo field is by agreement between the sender and
9/// recipient of the note.
10///
11/// [ps]: https://zips.z.cash/protocol/protocol.pdf#notept
12#[derive(Clone)]
13pub struct Memo(pub(crate) Box<[u8; 512]>);
14
15impl<'a> TryFrom<&'a [u8]> for Memo {
16    type Error = &'static str;
17
18    fn try_from(input: &'a [u8]) -> Result<Self, Self::Error> {
19        let mut full_bytes = [0; 512];
20
21        match input.len().cmp(&512) {
22            cmp::Ordering::Less => {
23                full_bytes[0..input.len()].copy_from_slice(input);
24                Ok(Memo(Box::new(full_bytes)))
25            }
26            cmp::Ordering::Equal => {
27                full_bytes[..].copy_from_slice(input);
28                Ok(Memo(Box::new(full_bytes)))
29            }
30            cmp::Ordering::Greater => Err("Memos have a max length of 512 bytes."),
31        }
32    }
33}
34
35impl fmt::Debug for Memo {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        // This saves work but if the 'valid utf8 string' is just a
38        // bunch of numbers, it prints them out like
39        // 'Memo("\u{0}\u{0}..")', so. ¯\_(ツ)_/¯
40        let output: String = match std::str::from_utf8(&self.0[..]) {
41            Ok(memo) => String::from(memo),
42            _ => hex::encode(&self.0[..]),
43        };
44
45        f.debug_tuple("Memo").field(&output).finish()
46    }
47}
48
49#[test]
50fn memo_fmt() {
51    let _init_guard = zebra_test::init();
52
53    // Rust changed the escaping of ' between 1.52 and 1.53 (nightly-2021-04-14?),
54    // so the memo string can't contain '
55    //
56    // TODO: rewrite this test so it doesn't depend on the exact debug format
57    let memo = Memo(Box::new(
58        *b"thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
59           iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
60           aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
61           veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy \
62           looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \
63           meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo \
64           but its just short enough!",
65    ));
66
67    assert_eq!(format!("{memo:?}"),
68               "Memo(\"thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo but its just short enough!\")"
69    );
70
71    let mut some_bytes = [0u8; 512];
72    some_bytes[0] = 0xF6;
73
74    assert_eq!(format!("{:?}", Memo(Box::new(some_bytes))),
75               "Memo(\"f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\")"
76    );
77}
78
79#[test]
80fn memo_from_string() {
81    let _init_guard = zebra_test::init();
82
83    let memo = Memo::try_from("foo bar baz".as_ref()).unwrap();
84
85    let mut bytes = [0; 512];
86    bytes[0..11].copy_from_slice(&[102, 111, 111, 32, 98, 97, 114, 32, 98, 97, 122]);
87
88    assert!(memo.0.iter().eq(bytes.iter()));
89}