Remove Default impl from MemoBytes

Memo fields have two ways to encode an empty memo:

- 0xF6 followed by all-zeroes, encoding "there is no memo".
- All-zeroes, encoding the empty UTF-8 string.

In almost all cases you want the former, but users thinking about byte
slices may expect MemoBytes::default() to result in the latter. To
ensure clarity, we now require calling either MemoBytes::default() or
MemoBytes::from_bytes(&[]) to be explicit.

No such confusion exists for the Memo enum, because the two types are
visibly separated as different enum cases, and Memo::Empty makes sense
as the default.
This commit is contained in:
Jack Grigg 2021-03-17 20:47:11 +13:00
parent c7a3ef0e88
commit 8a84203685
6 changed files with 22 additions and 19 deletions

View File

@ -345,7 +345,7 @@ mod tests {
Some(extfvk.fvk.ovk),
note.clone(),
to,
MemoBytes::default(),
MemoBytes::empty(),
&mut rng,
);
let cmu = note.cmu().to_repr().as_ref().to_owned();

View File

@ -603,7 +603,7 @@ mod tests {
Some(extfvk.fvk.ovk),
note.clone(),
to,
MemoBytes::default(),
MemoBytes::empty(),
&mut rng,
);
let cmu = note.cmu().to_repr().as_ref().to_vec();
@ -663,7 +663,7 @@ mod tests {
Some(extfvk.fvk.ovk),
note.clone(),
to,
MemoBytes::default(),
MemoBytes::empty(),
&mut rng,
);
let cmu = note.cmu().to_repr().as_ref().to_vec();
@ -691,7 +691,7 @@ mod tests {
Some(extfvk.fvk.ovk),
note.clone(),
change_addr,
MemoBytes::default(),
MemoBytes::empty(),
&mut rng,
);
let cmu = note.cmu().to_repr().as_ref().to_vec();

View File

@ -36,7 +36,7 @@ fn bench_note_decryption(c: &mut Criterion) {
let note = pa.create_note(value, rseed).unwrap();
let cmu = note.cmu();
let mut ne = SaplingNoteEncryption::new(None, note, pa, MemoBytes::default(), &mut rng);
let mut ne = SaplingNoteEncryption::new(None, note, pa, MemoBytes::empty(), &mut rng);
let ephemeral_key = ne.epk().clone().into();
let enc_ciphertext = ne.encrypt_note_plaintext();
let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu);

View File

@ -58,14 +58,6 @@ impl fmt::Debug for MemoBytes {
}
}
impl Default for MemoBytes {
fn default() -> Self {
let mut bytes = [0u8; 512];
bytes[0] = 0xF6;
MemoBytes(Box::new(bytes))
}
}
impl PartialEq for MemoBytes {
fn eq(&self, rhs: &MemoBytes) -> bool {
self.0[..] == rhs.0[..]
@ -87,10 +79,22 @@ impl Ord for MemoBytes {
}
impl MemoBytes {
/// Creates a `MemoBytes` from a slice.
/// Creates a `MemoBytes` indicating that no memo is present.
pub fn empty() -> Self {
let mut bytes = [0u8; 512];
bytes[0] = 0xF6;
MemoBytes(Box::new(bytes))
}
/// Creates a `MemoBytes` from a slice, exactly as provided.
///
/// Returns an error if the provided slice is longer than 512 bytes. Slices shorter
/// than 512 bytes are padded with null bytes.
///
/// Note that passing an empty slice to this API (or an all-zeroes slice) will result
/// in a memo representing an empty string. What you almost certainly want in this
/// case is [`MemoBytes::empty`], which uses a specific encoding to indicate that no
/// memo is present.
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() > 512 {
return Err(Error::TooLong(bytes.len()));
@ -220,7 +224,7 @@ impl From<&Memo> for MemoBytes {
/// Serializes the `Memo` per ZIP 302.
fn from(memo: &Memo) -> Self {
match memo {
Memo::Empty => MemoBytes::default(),
Memo::Empty => MemoBytes::empty(),
Memo::Text(s) => {
let mut bytes = [0u8; 512];
let s_bytes = s.0.as_bytes();

View File

@ -135,7 +135,7 @@ pub fn prf_ock(
/// let note = to.create_note(value, Rseed::BeforeZip212(rcm)).unwrap();
/// let cmu = note.cmu();
///
/// let mut enc = SaplingNoteEncryption::new(ovk, note, to, MemoBytes::default(), &mut rng);
/// let mut enc = SaplingNoteEncryption::new(ovk, note, to, MemoBytes::empty(), &mut rng);
/// let encCiphertext = enc.encrypt_note_plaintext();
/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.commitment().into(), &cmu);
/// ```
@ -681,8 +681,7 @@ mod tests {
let cmu = note.cmu();
let ovk = OutgoingViewingKey([0; 32]);
let mut ne =
SaplingNoteEncryption::new(Some(ovk), note, pa, MemoBytes::default(), &mut rng);
let mut ne = SaplingNoteEncryption::new(Some(ovk), note, pa, MemoBytes::empty(), &mut rng);
let epk = ne.epk().clone().into();
let enc_ciphertext = ne.encrypt_note_plaintext();
let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu);

View File

@ -142,7 +142,7 @@ impl SaplingOutput {
ovk,
to,
note,
memo: memo.unwrap_or_default(),
memo: memo.unwrap_or_else(MemoBytes::empty),
})
}