Merge pull request #469 from zcash/zcash_note_encryption-api-cleanups

`zcash_note_encryption` API cleanups
This commit is contained in:
str4d 2021-12-17 13:42:42 +00:00 committed by GitHub
commit 905c6e2dd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 97 additions and 90 deletions

View File

@ -20,7 +20,7 @@ codegen-units = 1
[patch.crates-io]
# In development.
orchard = { git = "https://github.com/zcash/orchard.git", rev = "8c018eff7e795b16fc68aed22d0fd4eebe2710ec" }
orchard = { git = "https://github.com/zcash/orchard.git", rev = "4b0b32275fe941b28cdfe632d2748453e6c32fbb" }
incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" }
zcash_encoding = { path = "components/zcash_encoding" }
zcash_note_encryption = { path = "components/zcash_note_encryption" }

View File

@ -28,6 +28,7 @@ jubjub = "0.8"
[features]
default = ["std"]
alloc = []
pre-zip-212 = []
std = ["alloc", "blake2b_simd/std"]
[lib]

View File

@ -5,14 +5,14 @@ use core::iter;
use crate::{
try_compact_note_decryption_inner, try_note_decryption_inner, BatchDomain, EphemeralKeyBytes,
ShieldedOutput,
ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE,
};
/// Trial decryption of a batch of notes with a set of recipients.
///
/// This is the batched version of [`crate::try_note_decryption`].
#[allow(clippy::type_complexity)]
pub fn try_note_decryption<D: BatchDomain, Output: ShieldedOutput<D>>(
pub fn try_note_decryption<D: BatchDomain, Output: ShieldedOutput<D, ENC_CIPHERTEXT_SIZE>>(
ivks: &[D::IncomingViewingKey],
outputs: &[(D, Output)],
) -> Vec<Option<(D::Note, D::Recipient, D::Memo)>> {
@ -22,14 +22,14 @@ pub fn try_note_decryption<D: BatchDomain, Output: ShieldedOutput<D>>(
/// Trial decryption of a batch of notes for light clients with a set of recipients.
///
/// This is the batched version of [`crate::try_compact_note_decryption`].
pub fn try_compact_note_decryption<D: BatchDomain, Output: ShieldedOutput<D>>(
pub fn try_compact_note_decryption<D: BatchDomain, Output: ShieldedOutput<D, COMPACT_NOTE_SIZE>>(
ivks: &[D::IncomingViewingKey],
outputs: &[(D, Output)],
) -> Vec<Option<(D::Note, D::Recipient)>> {
batch_note_decryption(ivks, outputs, try_compact_note_decryption_inner)
}
fn batch_note_decryption<D: BatchDomain, Output: ShieldedOutput<D>, F, FR>(
fn batch_note_decryption<D: BatchDomain, Output: ShieldedOutput<D, CS>, F, FR, const CS: usize>(
ivks: &[D::IncomingViewingKey],
outputs: &[(D, Output)],
decrypt_inner: F,

View File

@ -82,7 +82,7 @@ pub struct NotePlaintextBytes(pub [u8; NOTE_PLAINTEXT_SIZE]);
pub struct OutPlaintextBytes(pub [u8; OUT_PLAINTEXT_SIZE]);
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum NoteValidity {
enum NoteValidity {
Valid,
Invalid,
}
@ -148,11 +148,6 @@ pub trait Domain {
fn epk(ephemeral_key: &EphemeralKeyBytes) -> Option<Self::EphemeralPublicKey>;
fn check_epk_bytes<F: Fn(&Self::EphemeralSecretKey) -> NoteValidity>(
note: &Self::Note,
check: F,
) -> NoteValidity;
fn cmstar(note: &Self::Note) -> Self::ExtractedCommitment;
fn parse_note_plaintext_without_memo_ivk(
@ -166,19 +161,17 @@ pub trait Domain {
pk_d: &Self::DiversifiedTransmissionKey,
esk: &Self::EphemeralSecretKey,
ephemeral_key: &EphemeralKeyBytes,
plaintext: &[u8],
plaintext: &NotePlaintextBytes,
) -> Option<(Self::Note, Self::Recipient)>;
// &self is passed here in anticipation of future changes
// to memo handling where the memos may no longer be
// part of the note plaintext.
fn extract_memo(&self, plaintext: &[u8]) -> Self::Memo;
fn extract_memo(&self, plaintext: &NotePlaintextBytes) -> Self::Memo;
fn extract_pk_d(
out_plaintext: &[u8; OUT_PLAINTEXT_SIZE],
) -> Option<Self::DiversifiedTransmissionKey>;
fn extract_pk_d(out_plaintext: &OutPlaintextBytes) -> Option<Self::DiversifiedTransmissionKey>;
fn extract_esk(out_plaintext: &[u8; OUT_PLAINTEXT_SIZE]) -> Option<Self::EphemeralSecretKey>;
fn extract_esk(out_plaintext: &OutPlaintextBytes) -> Option<Self::EphemeralSecretKey>;
}
#[cfg(feature = "alloc")]
@ -213,10 +206,10 @@ pub trait BatchDomain: Domain {
}
}
pub trait ShieldedOutput<D: Domain> {
pub trait ShieldedOutput<D: Domain, const CIPHERTEXT_SIZE: usize> {
fn ephemeral_key(&self) -> EphemeralKeyBytes;
fn cmstar_bytes(&self) -> D::ExtractedCommitmentBytes;
fn enc_ciphertext(&self) -> &[u8];
fn enc_ciphertext(&self) -> &[u8; CIPHERTEXT_SIZE];
}
/// A struct containing context required for encrypting Sapling and Orchard notes.
@ -291,12 +284,21 @@ impl<D: Domain> NoteEncryption<D> {
memo: D::Memo,
) -> Self {
let esk = D::derive_esk(&note).expect("ZIP 212 is active.");
Self::new_with_esk(esk, ovk, note, to, memo)
NoteEncryption {
epk: D::ka_derive_public(&note, &esk),
esk,
note,
to,
memo,
ovk,
}
}
/// For use only with Sapling. This method is preserved in order that test code
/// be able to generate pre-ZIP-212 ciphertexts so that tests can continue to
/// cover pre-ZIP-212 transaction decryption.
#[cfg(feature = "pre-zip-212")]
#[cfg_attr(docsrs, doc(cfg(feature = "pre-zip-212")))]
pub fn new_with_esk(
esk: D::EphemeralSecretKey,
ovk: Option<D::OutgoingViewingKey>,
@ -387,7 +389,7 @@ impl<D: Domain> NoteEncryption<D> {
///
/// Implements section 4.19.2 of the
/// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#decryptivk).
pub fn try_note_decryption<D: Domain, Output: ShieldedOutput<D>>(
pub fn try_note_decryption<D: Domain, Output: ShieldedOutput<D, ENC_CIPHERTEXT_SIZE>>(
domain: &D,
ivk: &D::IncomingViewingKey,
output: &Output,
@ -401,7 +403,7 @@ pub fn try_note_decryption<D: Domain, Output: ShieldedOutput<D>>(
try_note_decryption_inner(domain, ivk, &ephemeral_key, output, key)
}
fn try_note_decryption_inner<D: Domain, Output: ShieldedOutput<D>>(
fn try_note_decryption_inner<D: Domain, Output: ShieldedOutput<D, ENC_CIPHERTEXT_SIZE>>(
domain: &D,
ivk: &D::IncomingViewingKey,
ephemeral_key: &EphemeralKeyBytes,
@ -409,16 +411,15 @@ fn try_note_decryption_inner<D: Domain, Output: ShieldedOutput<D>>(
key: D::SymmetricKey,
) -> Option<(D::Note, D::Recipient, D::Memo)> {
let enc_ciphertext = output.enc_ciphertext();
assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE);
let mut plaintext: [u8; NOTE_PLAINTEXT_SIZE] =
enc_ciphertext[..NOTE_PLAINTEXT_SIZE].try_into().unwrap();
let mut plaintext =
NotePlaintextBytes(enc_ciphertext[..NOTE_PLAINTEXT_SIZE].try_into().unwrap());
ChaCha20Poly1305::new(key.as_ref().into())
.decrypt_in_place_detached(
[0u8; 12][..].into(),
&[],
&mut plaintext,
&mut plaintext.0,
enc_ciphertext[NOTE_PLAINTEXT_SIZE..].into(),
)
.ok()?;
@ -428,7 +429,7 @@ fn try_note_decryption_inner<D: Domain, Output: ShieldedOutput<D>>(
ivk,
ephemeral_key,
&output.cmstar_bytes(),
&plaintext,
&plaintext.0,
)?;
let memo = domain.extract_memo(&plaintext);
@ -457,7 +458,7 @@ fn check_note_validity<D: Domain>(
cmstar_bytes: &D::ExtractedCommitmentBytes,
) -> NoteValidity {
if &D::ExtractedCommitmentBytes::from(&D::cmstar(&note)) == cmstar_bytes {
D::check_epk_bytes(&note, |derived_esk| {
if let Some(derived_esk) = D::derive_esk(note) {
if D::epk_bytes(&D::ka_derive_public(&note, &derived_esk))
.ct_eq(&ephemeral_key)
.into()
@ -466,7 +467,10 @@ fn check_note_validity<D: Domain>(
} else {
NoteValidity::Invalid
}
})
} else {
// Before ZIP 212
NoteValidity::Valid
}
} else {
// Published commitment doesn't match calculated commitment
NoteValidity::Invalid
@ -482,7 +486,7 @@ fn check_note_validity<D: Domain>(
/// Implements the procedure specified in [`ZIP 307`].
///
/// [`ZIP 307`]: https://zips.z.cash/zip-0307
pub fn try_compact_note_decryption<D: Domain, Output: ShieldedOutput<D>>(
pub fn try_compact_note_decryption<D: Domain, Output: ShieldedOutput<D, COMPACT_NOTE_SIZE>>(
domain: &D,
ivk: &D::IncomingViewingKey,
output: &Output,
@ -496,15 +500,13 @@ pub fn try_compact_note_decryption<D: Domain, Output: ShieldedOutput<D>>(
try_compact_note_decryption_inner(domain, ivk, &ephemeral_key, output, key)
}
fn try_compact_note_decryption_inner<D: Domain, Output: ShieldedOutput<D>>(
fn try_compact_note_decryption_inner<D: Domain, Output: ShieldedOutput<D, COMPACT_NOTE_SIZE>>(
domain: &D,
ivk: &D::IncomingViewingKey,
ephemeral_key: &EphemeralKeyBytes,
output: &Output,
key: D::SymmetricKey,
) -> Option<(D::Note, D::Recipient)> {
assert_eq!(output.enc_ciphertext().len(), COMPACT_NOTE_SIZE);
// Start from block 1 to skip over Poly1305 keying output
let mut plaintext = [0; COMPACT_NOTE_SIZE];
plaintext.copy_from_slice(output.enc_ciphertext());
@ -530,12 +532,12 @@ fn try_compact_note_decryption_inner<D: Domain, Output: ShieldedOutput<D>>(
/// Implements [Zcash Protocol Specification section 4.19.3][decryptovk].
///
/// [decryptovk]: https://zips.z.cash/protocol/nu5.pdf#decryptovk
pub fn try_output_recovery_with_ovk<D: Domain, Output: ShieldedOutput<D>>(
pub fn try_output_recovery_with_ovk<D: Domain, Output: ShieldedOutput<D, ENC_CIPHERTEXT_SIZE>>(
domain: &D,
ovk: &D::OutgoingViewingKey,
output: &Output,
cv: &D::ValueCommitment,
out_ciphertext: &[u8],
out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE],
) -> Option<(D::Note, D::Recipient, D::Memo)> {
let ock = D::derive_ock(ovk, &cv, &output.cmstar_bytes(), &output.ephemeral_key());
try_output_recovery_with_ock(domain, &ock, output, out_ciphertext)
@ -550,24 +552,22 @@ pub fn try_output_recovery_with_ovk<D: Domain, Output: ShieldedOutput<D>>(
/// Implements part of section 4.19.3 of the
/// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#decryptovk).
/// For decryption using a Full Viewing Key see [`try_output_recovery_with_ovk`].
pub fn try_output_recovery_with_ock<D: Domain, Output: ShieldedOutput<D>>(
pub fn try_output_recovery_with_ock<D: Domain, Output: ShieldedOutput<D, ENC_CIPHERTEXT_SIZE>>(
domain: &D,
ock: &OutgoingCipherKey,
output: &Output,
out_ciphertext: &[u8],
out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE],
) -> Option<(D::Note, D::Recipient, D::Memo)> {
let enc_ciphertext = output.enc_ciphertext();
assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE);
assert_eq!(out_ciphertext.len(), OUT_CIPHERTEXT_SIZE);
let mut op = [0; OUT_PLAINTEXT_SIZE];
op.copy_from_slice(&out_ciphertext[..OUT_PLAINTEXT_SIZE]);
let mut op = OutPlaintextBytes([0; OUT_PLAINTEXT_SIZE]);
op.0.copy_from_slice(&out_ciphertext[..OUT_PLAINTEXT_SIZE]);
ChaCha20Poly1305::new(ock.as_ref().into())
.decrypt_in_place_detached(
[0u8; 12][..].into(),
&[],
&mut op,
&mut op.0,
out_ciphertext[OUT_PLAINTEXT_SIZE..].into(),
)
.ok()?;
@ -582,14 +582,16 @@ pub fn try_output_recovery_with_ock<D: Domain, Output: ShieldedOutput<D>>(
// be okay.
let key = D::kdf(shared_secret, &ephemeral_key);
let mut plaintext = [0; NOTE_PLAINTEXT_SIZE];
plaintext.copy_from_slice(&enc_ciphertext[..NOTE_PLAINTEXT_SIZE]);
let mut plaintext = NotePlaintextBytes([0; NOTE_PLAINTEXT_SIZE]);
plaintext
.0
.copy_from_slice(&enc_ciphertext[..NOTE_PLAINTEXT_SIZE]);
ChaCha20Poly1305::new(key.as_ref().into())
.decrypt_in_place_detached(
[0u8; 12][..].into(),
&[],
&mut plaintext,
&mut plaintext.0,
enc_ciphertext[NOTE_PLAINTEXT_SIZE..].into(),
)
.ok()?;

View File

@ -127,7 +127,7 @@ impl TryFrom<compact_formats::CompactOutput> for CompactOutputDescription {
Ok(CompactOutputDescription {
cmu: value.cmu()?,
ephemeral_key: value.ephemeral_key()?,
enc_ciphertext: value.ciphertext,
enc_ciphertext: value.ciphertext.try_into().map_err(|_| ())?,
})
}
}

View File

@ -4,7 +4,7 @@ use ff::PrimeField;
use std::collections::HashSet;
use std::convert::TryFrom;
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_note_encryption::ShieldedOutput;
use zcash_note_encryption::{ShieldedOutput, COMPACT_NOTE_SIZE};
use zcash_primitives::{
consensus::{self, BlockHeight},
merkle_tree::{CommitmentTree, IncrementalWitness},
@ -109,7 +109,10 @@ pub trait ScanningKey {
/// Attempts to decrypt a Sapling note and payment address
/// from the specified ciphertext using this scanning key.
fn try_decryption<P: consensus::Parameters, Output: ShieldedOutput<SaplingDomain<P>>>(
fn try_decryption<
P: consensus::Parameters,
Output: ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>,
>(
&self,
params: &P,
height: BlockHeight,
@ -131,7 +134,10 @@ pub trait ScanningKey {
impl ScanningKey for ExtendedFullViewingKey {
type Nf = Nullifier;
fn try_decryption<P: consensus::Parameters, Output: ShieldedOutput<SaplingDomain<P>>>(
fn try_decryption<
P: consensus::Parameters,
Output: ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>,
>(
&self,
params: &P,
height: BlockHeight,
@ -152,7 +158,10 @@ impl ScanningKey for ExtendedFullViewingKey {
impl ScanningKey for SaplingIvk {
type Nf = ();
fn try_decryption<P: consensus::Parameters, Output: ShieldedOutput<SaplingDomain<P>>>(
fn try_decryption<
P: consensus::Parameters,
Output: ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>,
>(
&self,
params: &P,
height: BlockHeight,

View File

@ -45,7 +45,11 @@ secp256k1 = { version = "0.20", optional = true }
sha2 = "0.9"
subtle = "2.2.3"
zcash_encoding = { version = "0.0", path = "../components/zcash_encoding" }
zcash_note_encryption = { version = "0.0", path = "../components/zcash_note_encryption" }
[dependencies.zcash_note_encryption]
version = "0.0"
path = "../components/zcash_note_encryption"
features = ["pre-zip-212"]
[dev-dependencies]
criterion = "0.3"

View File

@ -10,8 +10,8 @@ use std::convert::TryInto;
use zcash_note_encryption::{
try_compact_note_decryption, try_note_decryption, try_output_recovery_with_ock,
try_output_recovery_with_ovk, BatchDomain, Domain, EphemeralKeyBytes, NoteEncryption,
NotePlaintextBytes, NoteValidity, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput,
COMPACT_NOTE_SIZE, NOTE_PLAINTEXT_SIZE, OUT_PLAINTEXT_SIZE,
NotePlaintextBytes, OutPlaintextBytes, OutgoingCipherKey, ShieldedOutput, COMPACT_NOTE_SIZE,
ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, OUT_PLAINTEXT_SIZE,
};
use crate::{
@ -247,18 +247,6 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
jubjub::ExtendedPoint::from_bytes(&ephemeral_key.0).into()
}
fn check_epk_bytes<F: FnOnce(&Self::EphemeralSecretKey) -> NoteValidity>(
note: &Note,
check: F,
) -> NoteValidity {
if let Some(derived_esk) = note.derive_esk() {
check(&derived_esk)
} else {
// Before ZIP 212
NoteValidity::Valid
}
}
fn parse_note_plaintext_without_memo_ivk(
&self,
ivk: &Self::IncomingViewingKey,
@ -274,9 +262,9 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
pk_d: &Self::DiversifiedTransmissionKey,
esk: &Self::EphemeralSecretKey,
ephemeral_key: &EphemeralKeyBytes,
plaintext: &[u8],
plaintext: &NotePlaintextBytes,
) -> Option<(Self::Note, Self::Recipient)> {
sapling_parse_note_plaintext_without_memo(&self, plaintext, |diversifier| {
sapling_parse_note_plaintext_without_memo(&self, &plaintext.0, |diversifier| {
if (diversifier.g_d()? * esk).to_bytes() == ephemeral_key.0 {
Some(*pk_d)
} else {
@ -289,29 +277,24 @@ impl<P: consensus::Parameters> Domain for SaplingDomain<P> {
note.cmu()
}
fn extract_pk_d(op: &[u8; OUT_PLAINTEXT_SIZE]) -> Option<Self::DiversifiedTransmissionKey> {
let pk_d = jubjub::SubgroupPoint::from_bytes(
op[0..32].try_into().expect("slice is the correct length"),
);
if pk_d.is_none().into() {
None
} else {
Some(pk_d.unwrap())
}
fn extract_pk_d(op: &OutPlaintextBytes) -> Option<Self::DiversifiedTransmissionKey> {
jubjub::SubgroupPoint::from_bytes(
op.0[0..32].try_into().expect("slice is the correct length"),
)
.into()
}
fn extract_esk(op: &[u8; OUT_PLAINTEXT_SIZE]) -> Option<Self::EphemeralSecretKey> {
fn extract_esk(op: &OutPlaintextBytes) -> Option<Self::EphemeralSecretKey> {
jubjub::Fr::from_repr(
op[32..OUT_PLAINTEXT_SIZE]
op.0[32..OUT_PLAINTEXT_SIZE]
.try_into()
.expect("slice is the correct length"),
)
.into()
}
fn extract_memo(&self, plaintext: &[u8]) -> Self::Memo {
MemoBytes::from_bytes(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]).unwrap()
fn extract_memo(&self, plaintext: &NotePlaintextBytes) -> Self::Memo {
MemoBytes::from_bytes(&plaintext.0[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]).unwrap()
}
}
@ -404,7 +387,7 @@ pub fn plaintext_version_is_valid<P: consensus::Parameters>(
pub fn try_sapling_note_decryption<
P: consensus::Parameters,
Output: ShieldedOutput<SaplingDomain<P>>,
Output: ShieldedOutput<SaplingDomain<P>, ENC_CIPHERTEXT_SIZE>,
>(
params: &P,
height: BlockHeight,
@ -420,7 +403,7 @@ pub fn try_sapling_note_decryption<
pub fn try_sapling_compact_note_decryption<
P: consensus::Parameters,
Output: ShieldedOutput<SaplingDomain<P>>,
Output: ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>,
>(
params: &P,
height: BlockHeight,

View File

@ -1,9 +1,13 @@
use core::fmt::Debug;
use std::convert::TryInto;
use ff::PrimeField;
use group::GroupEncoding;
use std::io::{self, Read, Write};
use zcash_note_encryption::{EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE};
use zcash_note_encryption::{
EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE,
};
use crate::{
consensus,
@ -219,7 +223,9 @@ pub struct OutputDescription<Proof> {
pub zkproof: Proof,
}
impl<P: consensus::Parameters, A> ShieldedOutput<SaplingDomain<P>> for OutputDescription<A> {
impl<P: consensus::Parameters, A> ShieldedOutput<SaplingDomain<P>, ENC_CIPHERTEXT_SIZE>
for OutputDescription<A>
{
fn ephemeral_key(&self) -> EphemeralKeyBytes {
self.ephemeral_key.clone()
}
@ -228,7 +234,7 @@ impl<P: consensus::Parameters, A> ShieldedOutput<SaplingDomain<P>> for OutputDes
self.cmu.to_repr()
}
fn enc_ciphertext(&self) -> &[u8] {
fn enc_ciphertext(&self) -> &[u8; ENC_CIPHERTEXT_SIZE] {
&self.enc_ciphertext
}
}
@ -347,7 +353,7 @@ impl OutputDescriptionV5 {
pub struct CompactOutputDescription {
pub ephemeral_key: EphemeralKeyBytes,
pub cmu: bls12_381::Scalar,
pub enc_ciphertext: Vec<u8>,
pub enc_ciphertext: [u8; COMPACT_NOTE_SIZE],
}
impl<A> From<OutputDescription<A>> for CompactOutputDescription {
@ -355,12 +361,14 @@ impl<A> From<OutputDescription<A>> for CompactOutputDescription {
CompactOutputDescription {
ephemeral_key: out.ephemeral_key,
cmu: out.cmu,
enc_ciphertext: out.enc_ciphertext[..COMPACT_NOTE_SIZE].to_vec(),
enc_ciphertext: out.enc_ciphertext[..COMPACT_NOTE_SIZE].try_into().unwrap(),
}
}
}
impl<P: consensus::Parameters> ShieldedOutput<SaplingDomain<P>> for CompactOutputDescription {
impl<P: consensus::Parameters> ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>
for CompactOutputDescription
{
fn ephemeral_key(&self) -> EphemeralKeyBytes {
self.ephemeral_key.clone()
}
@ -369,7 +377,7 @@ impl<P: consensus::Parameters> ShieldedOutput<SaplingDomain<P>> for CompactOutpu
self.cmu.to_repr()
}
fn enc_ciphertext(&self) -> &[u8] {
fn enc_ciphertext(&self) -> &[u8; COMPACT_NOTE_SIZE] {
&self.enc_ciphertext
}
}