penumbra_tct/internal/hash/option.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
use ark_ff::{BigInt, PrimeField};
use decaf377::Fq;
use std::fmt::Debug;
use crate::prelude::*;
/// A representation of `Option<Hash>` without the tag bytes required by `Option`, because we
/// know that no valid [`struct@Hash`] will be equal to `[u64::MAX; 4]`, since the modulus for
/// [`Commitment`](crate::Commitment) is too small.
///
/// This type is inter-convertible via [`From`] and [`Into`] with `Option<Hash>`, and that is
/// its only purpose.
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(from = "Option<Hash>", into = "Option<Hash>")]
pub struct OptionHash {
inner: [u64; 4],
}
impl Debug for OptionHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<Option<Hash>>::from(*self).fmt(f)
}
}
impl Default for OptionHash {
fn default() -> Self {
Self {
inner: [u64::MAX; 4],
}
}
}
impl From<Option<Hash>> for OptionHash {
fn from(hash: Option<Hash>) -> Self {
match hash {
Some(hash) => Self {
inner: Fq::into_bigint(hash.0).0,
},
None => Self {
// This sentinel value is not a valid `Fq` because it's bigger than the modulus,
// which means that it will never occur otherwise
inner: [u64::MAX; 4],
},
}
}
}
impl From<OptionHash> for Option<Hash> {
fn from(hash: OptionHash) -> Self {
if hash.inner == [u64::MAX; 4] {
None
} else {
// We're directly constructing the hash here by coercing the bytes into the right type,
// but this is safe because we know that the bytes are a real `Fq` and not the sentinel
// value we just checked for
Some(Hash::new(
Fq::from_bigint(BigInt::new(hash.inner))
.expect("convert bytes to a valid Fq element"),
))
}
}
}