2021-03-05 15:25:45 -08:00
|
|
|
//! Helper functions defined in the Zcash Protocol Specification.
|
|
|
|
|
2022-04-28 13:20:23 -07:00
|
|
|
use core::iter;
|
|
|
|
use core::ops::Deref;
|
2021-03-05 15:25:45 -08:00
|
|
|
|
2023-02-28 14:21:37 -08:00
|
|
|
use ff::{Field, FromUniformBytes, PrimeField, PrimeFieldBits};
|
2022-10-15 14:00:09 -07:00
|
|
|
use group::{Curve, Group, GroupEncoding, WnafBase, WnafScalar};
|
2022-05-10 13:53:49 -07:00
|
|
|
use halo2_gadgets::{poseidon::primitives as poseidon, sinsemilla::primitives as sinsemilla};
|
2023-02-28 14:21:37 -08:00
|
|
|
use halo2_proofs::arithmetic::{CurveAffine, CurveExt};
|
2022-10-15 16:29:06 -07:00
|
|
|
use memuse::DynamicUsage;
|
2021-03-17 19:06:16 -07:00
|
|
|
use pasta_curves::pallas;
|
2021-06-02 14:31:18 -07:00
|
|
|
use subtle::{ConditionallySelectable, CtOption};
|
2021-03-05 15:25:45 -08:00
|
|
|
|
2022-01-28 07:53:03 -08:00
|
|
|
use crate::constants::{
|
|
|
|
fixed_bases::COMMIT_IVK_PERSONALIZATION, util::gen_const_array,
|
|
|
|
KEY_DIVERSIFICATION_PERSONALIZATION, L_ORCHARD_BASE,
|
2021-03-15 18:27:08 -07:00
|
|
|
};
|
2021-03-05 15:25:45 -08:00
|
|
|
|
2021-05-28 04:42:01 -07:00
|
|
|
mod prf_expand;
|
2021-05-28 05:11:54 -07:00
|
|
|
pub(crate) use prf_expand::PrfExpand;
|
2021-03-05 15:25:45 -08:00
|
|
|
|
2021-05-11 01:06:16 -07:00
|
|
|
/// A Pallas point that is guaranteed to not be the identity.
|
2021-06-10 11:16:08 -07:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
2021-05-11 01:06:16 -07:00
|
|
|
pub(crate) struct NonIdentityPallasPoint(pallas::Point);
|
|
|
|
|
2021-06-10 11:16:08 -07:00
|
|
|
impl Default for NonIdentityPallasPoint {
|
|
|
|
fn default() -> Self {
|
|
|
|
NonIdentityPallasPoint(pallas::Point::generator())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 14:31:18 -07:00
|
|
|
impl ConditionallySelectable for NonIdentityPallasPoint {
|
|
|
|
fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
|
|
|
|
NonIdentityPallasPoint(pallas::Point::conditional_select(&a.0, &b.0, choice))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NonIdentityPallasPoint {
|
|
|
|
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
|
|
|
pallas::Point::from_bytes(bytes)
|
|
|
|
.and_then(|p| CtOption::new(NonIdentityPallasPoint(p), !p.is_identity()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 01:06:16 -07:00
|
|
|
impl Deref for NonIdentityPallasPoint {
|
|
|
|
type Target = pallas::Point;
|
|
|
|
|
|
|
|
fn deref(&self) -> &pallas::Point {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 00:08:39 -07:00
|
|
|
/// An integer in [1..q_P].
|
2021-06-10 11:16:08 -07:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
2021-05-11 00:08:39 -07:00
|
|
|
pub(crate) struct NonZeroPallasBase(pallas::Base);
|
|
|
|
|
2021-06-10 11:16:08 -07:00
|
|
|
impl Default for NonZeroPallasBase {
|
|
|
|
fn default() -> Self {
|
|
|
|
NonZeroPallasBase(pallas::Base::one())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConditionallySelectable for NonZeroPallasBase {
|
|
|
|
fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
|
|
|
|
NonZeroPallasBase(pallas::Base::conditional_select(&a.0, &b.0, choice))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 00:08:39 -07:00
|
|
|
impl NonZeroPallasBase {
|
2021-06-10 11:16:08 -07:00
|
|
|
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
2021-12-07 10:02:03 -08:00
|
|
|
pallas::Base::from_repr(*bytes).and_then(NonZeroPallasBase::from_base)
|
2021-06-10 11:16:08 -07:00
|
|
|
}
|
|
|
|
|
2022-01-20 07:16:45 -08:00
|
|
|
pub(crate) fn to_bytes(self) -> [u8; 32] {
|
2021-12-07 10:02:03 -08:00
|
|
|
self.0.to_repr()
|
2021-07-20 12:37:07 -07:00
|
|
|
}
|
|
|
|
|
2021-06-10 11:16:08 -07:00
|
|
|
pub(crate) fn from_base(b: pallas::Base) -> CtOption<Self> {
|
2021-09-06 12:18:18 -07:00
|
|
|
CtOption::new(NonZeroPallasBase(b), !b.is_zero())
|
2021-06-10 11:16:08 -07:00
|
|
|
}
|
|
|
|
|
2021-05-11 00:08:39 -07:00
|
|
|
/// Constructs a wrapper for a base field element that is guaranteed to be non-zero.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if `s.is_zero()`.
|
|
|
|
fn guaranteed(s: pallas::Base) -> Self {
|
2021-09-06 12:18:18 -07:00
|
|
|
assert!(!bool::from(s.is_zero()));
|
2021-05-11 00:08:39 -07:00
|
|
|
NonZeroPallasBase(s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An integer in [1..r_P].
|
2021-07-26 11:05:28 -07:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
2021-05-11 00:08:39 -07:00
|
|
|
pub(crate) struct NonZeroPallasScalar(pallas::Scalar);
|
|
|
|
|
2021-06-10 11:16:08 -07:00
|
|
|
impl Default for NonZeroPallasScalar {
|
|
|
|
fn default() -> Self {
|
|
|
|
NonZeroPallasScalar(pallas::Scalar::one())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 00:08:39 -07:00
|
|
|
impl From<NonZeroPallasBase> for NonZeroPallasScalar {
|
|
|
|
fn from(s: NonZeroPallasBase) -> Self {
|
|
|
|
NonZeroPallasScalar::guaranteed(mod_r_p(s.0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 14:31:18 -07:00
|
|
|
impl ConditionallySelectable for NonZeroPallasScalar {
|
|
|
|
fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
|
|
|
|
NonZeroPallasScalar(pallas::Scalar::conditional_select(&a.0, &b.0, choice))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 00:08:39 -07:00
|
|
|
impl NonZeroPallasScalar {
|
2021-06-02 14:31:18 -07:00
|
|
|
pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
|
2021-12-07 10:02:03 -08:00
|
|
|
pallas::Scalar::from_repr(*bytes).and_then(NonZeroPallasScalar::from_scalar)
|
2021-06-02 14:31:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn from_scalar(s: pallas::Scalar) -> CtOption<Self> {
|
2021-09-06 12:18:18 -07:00
|
|
|
CtOption::new(NonZeroPallasScalar(s), !s.is_zero())
|
2021-06-02 14:31:18 -07:00
|
|
|
}
|
|
|
|
|
2021-05-11 00:08:39 -07:00
|
|
|
/// Constructs a wrapper for a scalar field element that is guaranteed to be non-zero.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if `s.is_zero()`.
|
|
|
|
fn guaranteed(s: pallas::Scalar) -> Self {
|
2021-09-06 12:18:18 -07:00
|
|
|
assert!(!bool::from(s.is_zero()));
|
2021-05-11 00:08:39 -07:00
|
|
|
NonZeroPallasScalar(s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Deref for NonZeroPallasScalar {
|
|
|
|
type Target = pallas::Scalar;
|
|
|
|
|
|
|
|
fn deref(&self) -> &pallas::Scalar {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-15 14:00:09 -07:00
|
|
|
const PREPARED_WINDOW_SIZE: usize = 4;
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub(crate) struct PreparedNonIdentityBase(WnafBase<pallas::Point, PREPARED_WINDOW_SIZE>);
|
|
|
|
|
|
|
|
impl PreparedNonIdentityBase {
|
|
|
|
pub(crate) fn new(base: NonIdentityPallasPoint) -> Self {
|
|
|
|
PreparedNonIdentityBase(WnafBase::new(base.0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub(crate) struct PreparedNonZeroScalar(WnafScalar<pallas::Scalar, PREPARED_WINDOW_SIZE>);
|
|
|
|
|
2022-10-15 16:29:06 -07:00
|
|
|
impl DynamicUsage for PreparedNonZeroScalar {
|
|
|
|
fn dynamic_usage(&self) -> usize {
|
|
|
|
self.0.dynamic_usage()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
|
|
|
self.0.dynamic_usage_bounds()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-15 14:00:09 -07:00
|
|
|
impl PreparedNonZeroScalar {
|
|
|
|
pub(crate) fn new(scalar: &NonZeroPallasScalar) -> Self {
|
|
|
|
PreparedNonZeroScalar(WnafScalar::new(scalar))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-05 15:25:45 -08:00
|
|
|
/// $\mathsf{ToBase}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod q_P)$
|
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-08 13:33:56 -08:00
|
|
|
pub(crate) fn to_base(x: [u8; 64]) -> pallas::Base {
|
2023-02-28 14:21:37 -08:00
|
|
|
pallas::Base::from_uniform_bytes(&x)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// $\mathsf{ToScalar}^\mathsf{Orchard}(x) := LEOS2IP_{\ell_\mathsf{PRFexpand}}(x) (mod r_P)$
|
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
2021-03-08 13:33:56 -08:00
|
|
|
pub(crate) fn to_scalar(x: [u8; 64]) -> pallas::Scalar {
|
2023-02-28 14:21:37 -08:00
|
|
|
pallas::Scalar::from_uniform_bytes(&x)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
2021-03-15 18:24:50 -07:00
|
|
|
/// Converts from pallas::Base to pallas::Scalar (aka $x \pmod{r_\mathbb{P}}$).
|
|
|
|
///
|
|
|
|
/// This requires no modular reduction because Pallas' base field is smaller than its
|
|
|
|
/// scalar field.
|
|
|
|
pub(crate) fn mod_r_p(x: pallas::Base) -> pallas::Scalar {
|
|
|
|
pallas::Scalar::from_repr(x.to_repr()).unwrap()
|
|
|
|
}
|
|
|
|
|
2022-03-17 07:39:15 -07:00
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.8.4: Sinsemilla commitments][concretesinsemillacommit].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2022-03-17 07:39:15 -07:00
|
|
|
/// [concretesinsemillacommit]: https://zips.z.cash/protocol/protocol.pdf#concretesinsemillacommit
|
2021-03-05 15:25:45 -08:00
|
|
|
pub(crate) fn commit_ivk(
|
2021-03-15 13:33:07 -07:00
|
|
|
ak: &pallas::Base,
|
2021-03-05 15:25:45 -08:00
|
|
|
nk: &pallas::Base,
|
|
|
|
rivk: &pallas::Scalar,
|
2022-03-17 07:39:15 -07:00
|
|
|
) -> CtOption<pallas::Base> {
|
2021-03-15 13:33:07 -07:00
|
|
|
// We rely on the API contract that to_le_bits() returns at least PrimeField::NUM_BITS
|
|
|
|
// bits, which is equal to L_ORCHARD_BASE.
|
2021-06-30 04:50:23 -07:00
|
|
|
let domain = sinsemilla::CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
|
2022-03-17 07:39:15 -07:00
|
|
|
domain.short_commit(
|
|
|
|
iter::empty()
|
2022-05-05 09:47:56 -07:00
|
|
|
.chain(ak.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE))
|
|
|
|
.chain(nk.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE)),
|
2022-03-17 07:39:15 -07:00
|
|
|
rivk,
|
|
|
|
)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
2021-03-05 17:17:51 -08:00
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [concretediversifyhash]: https://zips.z.cash/protocol/nu5.pdf#concretediversifyhash
|
2021-05-11 01:06:16 -07:00
|
|
|
pub(crate) fn diversify_hash(d: &[u8; 11]) -> NonIdentityPallasPoint {
|
2021-06-30 04:50:23 -07:00
|
|
|
let hasher = pallas::Point::hash_to_curve(KEY_DIVERSIFICATION_PERSONALIZATION);
|
2023-04-10 17:23:33 -07:00
|
|
|
let g_d = hasher(d);
|
2021-05-11 01:06:16 -07:00
|
|
|
// If the identity occurs, we replace it with a different fixed point.
|
2021-05-21 13:24:08 -07:00
|
|
|
// TODO: Replace the unwrap_or_else with a cached fixed point.
|
2023-04-10 17:23:33 -07:00
|
|
|
NonIdentityPallasPoint(CtOption::new(g_d, !g_d.is_identity()).unwrap_or_else(|| hasher(&[])))
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
2021-03-15 18:27:08 -07:00
|
|
|
/// $PRF^\mathsf{nfOrchard}(nk, \rho) := Poseidon(nk, \rho)$
|
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs].
|
|
|
|
///
|
2021-03-29 17:54:23 -07:00
|
|
|
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
|
2021-03-15 18:27:08 -07:00
|
|
|
pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base {
|
2021-12-09 12:23:27 -08:00
|
|
|
poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
|
2021-11-03 06:12:24 -07:00
|
|
|
.hash([nk, rho])
|
2021-03-15 18:27:08 -07:00
|
|
|
}
|
|
|
|
|
2021-03-17 12:20:40 -07:00
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-05 17:17:51 -08:00
|
|
|
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
|
2021-05-11 01:06:16 -07:00
|
|
|
pub(crate) fn ka_orchard(
|
|
|
|
sk: &NonZeroPallasScalar,
|
|
|
|
b: &NonIdentityPallasPoint,
|
|
|
|
) -> NonIdentityPallasPoint {
|
2022-10-15 14:00:09 -07:00
|
|
|
ka_orchard_prepared(
|
|
|
|
&PreparedNonZeroScalar::new(sk),
|
|
|
|
&PreparedNonIdentityBase::new(*b),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].
|
|
|
|
///
|
|
|
|
/// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement
|
|
|
|
pub(crate) fn ka_orchard_prepared(
|
|
|
|
sk: &PreparedNonZeroScalar,
|
|
|
|
b: &PreparedNonIdentityBase,
|
|
|
|
) -> NonIdentityPallasPoint {
|
|
|
|
NonIdentityPallasPoint(&b.0 * &sk.0)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
|
|
|
|
2021-03-17 12:20:40 -07:00
|
|
|
/// Coordinate extractor for Pallas.
|
2021-03-05 15:25:45 -08:00
|
|
|
///
|
2021-03-17 12:20:40 -07:00
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas].
|
2021-03-05 17:17:51 -08:00
|
|
|
///
|
|
|
|
/// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
|
2021-03-05 15:25:45 -08:00
|
|
|
pub(crate) fn extract_p(point: &pallas::Point) -> pallas::Base {
|
2021-04-19 15:02:59 -07:00
|
|
|
point
|
|
|
|
.to_affine()
|
|
|
|
.coordinates()
|
|
|
|
.map(|c| *c.x())
|
|
|
|
.unwrap_or_else(pallas::Base::zero)
|
2021-03-05 15:25:45 -08:00
|
|
|
}
|
2021-03-17 12:15:55 -07:00
|
|
|
|
2021-04-19 15:05:56 -07:00
|
|
|
/// Coordinate extractor for Pallas.
|
|
|
|
///
|
|
|
|
/// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas].
|
|
|
|
///
|
|
|
|
/// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas
|
|
|
|
pub(crate) fn extract_p_bottom(point: CtOption<pallas::Point>) -> CtOption<pallas::Base> {
|
|
|
|
point.map(|p| extract_p(&p))
|
|
|
|
}
|
|
|
|
|
2021-06-13 06:06:34 -07:00
|
|
|
/// The field element representation of a u64 integer represented by
|
|
|
|
/// an L-bit little-endian bitstring.
|
2021-12-07 09:47:03 -08:00
|
|
|
pub fn lebs2ip_field<F: PrimeField, const L: usize>(bits: &[bool; L]) -> F {
|
|
|
|
F::from(lebs2ip::<L>(bits))
|
2021-06-13 06:06:34 -07:00
|
|
|
}
|
|
|
|
|
2021-06-13 08:19:12 -07:00
|
|
|
/// The u64 integer represented by an L-bit little-endian bitstring.
|
|
|
|
///
|
2021-06-14 09:16:19 -07:00
|
|
|
/// # Panics
|
2021-06-13 08:19:12 -07:00
|
|
|
///
|
|
|
|
/// Panics if the bitstring is longer than 64 bits.
|
|
|
|
pub fn lebs2ip<const L: usize>(bits: &[bool; L]) -> u64 {
|
|
|
|
assert!(L <= 64);
|
|
|
|
bits.iter()
|
|
|
|
.enumerate()
|
|
|
|
.fold(0u64, |acc, (i, b)| acc + if *b { 1 << i } else { 0 })
|
|
|
|
}
|
|
|
|
|
2021-06-04 09:51:01 -07:00
|
|
|
/// The sequence of bits representing a u64 in little-endian order.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if the expected length of the sequence `NUM_BITS` exceeds
|
|
|
|
/// 64.
|
|
|
|
pub fn i2lebsp<const NUM_BITS: usize>(int: u64) -> [bool; NUM_BITS] {
|
|
|
|
assert!(NUM_BITS <= 64);
|
|
|
|
gen_const_array(|mask: usize| (int & (1 << mask)) != 0)
|
|
|
|
}
|
|
|
|
|
2021-03-17 12:15:55 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-06-04 09:51:01 -07:00
|
|
|
use super::{i2lebsp, lebs2ip};
|
|
|
|
|
2021-03-17 12:15:55 -07:00
|
|
|
use group::Group;
|
2022-01-28 07:53:03 -08:00
|
|
|
use halo2_proofs::arithmetic::CurveExt;
|
2021-03-17 19:06:16 -07:00
|
|
|
use pasta_curves::pallas;
|
2021-06-04 09:51:01 -07:00
|
|
|
use rand::{rngs::OsRng, RngCore};
|
2021-03-17 12:15:55 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn diversify_hash_substitution() {
|
|
|
|
assert!(!bool::from(
|
|
|
|
pallas::Point::hash_to_curve("z.cash:Orchard-gd")(&[]).is_identity()
|
|
|
|
));
|
|
|
|
}
|
2021-06-04 09:51:01 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn lebs2ip_round_trip() {
|
|
|
|
let mut rng = OsRng;
|
|
|
|
{
|
|
|
|
let int = rng.next_u64();
|
|
|
|
assert_eq!(lebs2ip::<64>(&i2lebsp(int)), int);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(lebs2ip::<64>(&i2lebsp(0)), 0);
|
|
|
|
assert_eq!(
|
|
|
|
lebs2ip::<64>(&i2lebsp(0xFFFFFFFFFFFFFFFF)),
|
|
|
|
0xFFFFFFFFFFFFFFFF
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn i2lebsp_round_trip() {
|
|
|
|
{
|
2022-04-28 13:58:31 -07:00
|
|
|
let bitstring = [0; 64].map(|_| rand::random());
|
|
|
|
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
|
2021-06-04 09:51:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let bitstring = [false; 64];
|
|
|
|
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let bitstring = [true; 64];
|
|
|
|
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let bitstring = [];
|
|
|
|
assert_eq!(i2lebsp(lebs2ip(&bitstring)), bitstring);
|
|
|
|
}
|
|
|
|
}
|
2021-03-17 12:15:55 -07:00
|
|
|
}
|