Use ShieldedOutput trait for note encryption/decryption.
This change modifies note encryption and decryption functions to treat a shielded output as a single value instead of handling the parts of an output as independent arguments.
This commit is contained in:
parent
4f22f1d578
commit
324fc36521
|
@ -5,7 +5,7 @@
|
|||
|
||||
use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf};
|
||||
use rand_core::RngCore;
|
||||
use subtle::{ConstantTimeEq, Choice};
|
||||
use subtle::{Choice, ConstantTimeEq};
|
||||
|
||||
pub const COMPACT_NOTE_SIZE: usize = 1 + // version
|
||||
11 + // diversifier
|
||||
|
@ -148,10 +148,10 @@ pub trait Domain {
|
|||
fn extract_esk(out_plaintext: &[u8; OUT_CIPHERTEXT_SIZE]) -> Option<Self::EphemeralSecretKey>;
|
||||
}
|
||||
|
||||
pub trait ShieldedOutput<'a, D: Domain> {
|
||||
fn ivk(&'a self) -> &'a D::IncomingViewingKey;
|
||||
fn epk(&'a self) -> &'a D::EphemeralPublicKey;
|
||||
fn cmstar(&'a self) -> &'a D::ExtractedCommitment;
|
||||
pub trait ShieldedOutput<D: Domain> {
|
||||
fn epk(&self) -> &D::EphemeralPublicKey;
|
||||
fn cmstar(&self) -> D::ExtractedCommitment;
|
||||
fn enc_ciphertext(&self) -> &[u8];
|
||||
}
|
||||
|
||||
/// A struct containing context required for encrypting Sapling and Orchard notes.
|
||||
|
@ -315,25 +315,22 @@ impl<D: Domain> NoteEncryption<D> {
|
|||
/// `PaymentAddress` to which the note was sent.
|
||||
///
|
||||
/// Implements section 4.17.2 of the Zcash Protocol Specification.
|
||||
pub fn try_note_decryption<D: Domain>(
|
||||
pub fn try_note_decryption<D: Domain, Output: ShieldedOutput<D>>(
|
||||
domain: &D,
|
||||
//output: &ShieldedOutput<D>,
|
||||
ivk: &D::IncomingViewingKey,
|
||||
epk: &D::EphemeralPublicKey,
|
||||
cmstar: &D::ExtractedCommitment,
|
||||
enc_ciphertext: &[u8],
|
||||
output: &Output,
|
||||
) -> Option<(D::Note, D::Recipient, D::Memo)> {
|
||||
assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE);
|
||||
assert_eq!(output.enc_ciphertext().len(), ENC_CIPHERTEXT_SIZE);
|
||||
|
||||
let shared_secret = D::ka_agree_dec(ivk, epk);
|
||||
let key = D::kdf(shared_secret, epk);
|
||||
let shared_secret = D::ka_agree_dec(ivk, output.epk());
|
||||
let key = D::kdf(shared_secret, output.epk());
|
||||
|
||||
let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
|
||||
assert_eq!(
|
||||
ChachaPolyIetf::aead_cipher()
|
||||
.open_to(
|
||||
&mut plaintext,
|
||||
&enc_ciphertext,
|
||||
output.enc_ciphertext(),
|
||||
&[],
|
||||
key.as_ref(),
|
||||
&[0u8; 12]
|
||||
|
@ -342,7 +339,13 @@ pub fn try_note_decryption<D: Domain>(
|
|||
NOTE_PLAINTEXT_SIZE
|
||||
);
|
||||
|
||||
let (note, to) = parse_note_plaintext_without_memo_ivk(domain, ivk, epk, cmstar, &plaintext)?;
|
||||
let (note, to) = parse_note_plaintext_without_memo_ivk(
|
||||
domain,
|
||||
ivk,
|
||||
output.epk(),
|
||||
&output.cmstar(),
|
||||
&plaintext,
|
||||
)?;
|
||||
let memo = domain.extract_memo(&plaintext);
|
||||
|
||||
Some((note, to, memo))
|
||||
|
@ -375,7 +378,10 @@ fn check_note_validity<D: Domain>(
|
|||
} else {
|
||||
let epk_bytes = D::epk_bytes(epk);
|
||||
D::check_epk_bytes(¬e, |derived_esk| {
|
||||
if D::epk_bytes(&D::ka_derive_public(¬e, &derived_esk)).ct_eq(&epk_bytes).into() {
|
||||
if D::epk_bytes(&D::ka_derive_public(¬e, &derived_esk))
|
||||
.ct_eq(&epk_bytes)
|
||||
.into()
|
||||
{
|
||||
NoteValidity::Valid
|
||||
} else {
|
||||
NoteValidity::Invalid
|
||||
|
@ -393,24 +399,22 @@ 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>(
|
||||
pub fn try_compact_note_decryption<D: Domain, Output: ShieldedOutput<D>>(
|
||||
domain: &D,
|
||||
ivk: &D::IncomingViewingKey,
|
||||
epk: &D::EphemeralPublicKey,
|
||||
cmstar: &D::ExtractedCommitment,
|
||||
enc_ciphertext: &[u8],
|
||||
output: &Output,
|
||||
) -> Option<(D::Note, D::Recipient)> {
|
||||
assert_eq!(enc_ciphertext.len(), COMPACT_NOTE_SIZE);
|
||||
assert_eq!(output.enc_ciphertext().len(), COMPACT_NOTE_SIZE);
|
||||
|
||||
let shared_secret = D::ka_agree_dec(&ivk, epk);
|
||||
let key = D::kdf(shared_secret, &epk);
|
||||
let shared_secret = D::ka_agree_dec(&ivk, output.epk());
|
||||
let key = D::kdf(shared_secret, output.epk());
|
||||
|
||||
// Start from block 1 to skip over Poly1305 keying output
|
||||
let mut plaintext = [0; COMPACT_NOTE_SIZE];
|
||||
plaintext.copy_from_slice(&enc_ciphertext);
|
||||
plaintext.copy_from_slice(output.enc_ciphertext());
|
||||
ChaCha20Ietf::xor(key.as_ref(), &[0u8; 12], 1, &mut plaintext);
|
||||
|
||||
parse_note_plaintext_without_memo_ivk(domain, ivk, epk, cmstar, &plaintext)
|
||||
parse_note_plaintext_without_memo_ivk(domain, ivk, output.epk(), &output.cmstar(), &plaintext)
|
||||
}
|
||||
|
||||
/// Recovery of the full note plaintext by the sender.
|
||||
|
@ -421,15 +425,13 @@ pub fn try_compact_note_decryption<D: Domain>(
|
|||
///
|
||||
/// Implements part of section 4.17.3 of the Zcash Protocol Specification.
|
||||
/// For decryption using a Full Viewing Key see [`try_sapling_output_recovery`].
|
||||
pub fn try_output_recovery_with_ock<D: Domain>(
|
||||
pub fn try_output_recovery_with_ock<D: Domain, Output: ShieldedOutput<D>>(
|
||||
domain: &D,
|
||||
ock: &OutgoingCipherKey,
|
||||
cmstar: &D::ExtractedCommitment,
|
||||
epk: &D::EphemeralPublicKey,
|
||||
enc_ciphertext: &[u8],
|
||||
output: &Output,
|
||||
out_ciphertext: &[u8],
|
||||
) -> Option<(D::Note, D::Recipient, D::Memo)> {
|
||||
assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE);
|
||||
assert_eq!(output.enc_ciphertext().len(), ENC_CIPHERTEXT_SIZE);
|
||||
assert_eq!(out_ciphertext.len(), OUT_CIPHERTEXT_SIZE);
|
||||
|
||||
let mut op = [0; OUT_CIPHERTEXT_SIZE];
|
||||
|
@ -444,14 +446,14 @@ pub fn try_output_recovery_with_ock<D: Domain>(
|
|||
let esk = D::extract_esk(&op)?;
|
||||
|
||||
let shared_secret = D::ka_agree_enc(&esk, &pk_d);
|
||||
let key = D::kdf(shared_secret, &epk);
|
||||
let key = D::kdf(shared_secret, output.epk());
|
||||
|
||||
let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
|
||||
assert_eq!(
|
||||
ChachaPolyIetf::aead_cipher()
|
||||
.open_to(
|
||||
&mut plaintext,
|
||||
&enc_ciphertext,
|
||||
output.enc_ciphertext(),
|
||||
&[],
|
||||
key.as_ref(),
|
||||
&[0u8; 12]
|
||||
|
@ -460,10 +462,11 @@ pub fn try_output_recovery_with_ock<D: Domain>(
|
|||
NOTE_PLAINTEXT_SIZE
|
||||
);
|
||||
|
||||
let (note, to) = domain.parse_note_plaintext_without_memo_ovk(&pk_d, &esk, &epk, &plaintext)?;
|
||||
let (note, to) =
|
||||
domain.parse_note_plaintext_without_memo_ovk(&pk_d, &esk, output.epk(), &plaintext)?;
|
||||
let memo = domain.extract_memo(&plaintext);
|
||||
|
||||
if let NoteValidity::Valid = check_note_validity::<D>(¬e, epk, cmstar) {
|
||||
if let NoteValidity::Valid = check_note_validity::<D>(¬e, output.epk(), &output.cmstar()) {
|
||||
Some((note, to, memo))
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -22,13 +22,14 @@ group = "0.8"
|
|||
hex = "0.4"
|
||||
jubjub = "0.5.1"
|
||||
nom = "6.1"
|
||||
percent-encoding = "2.1.0"
|
||||
proptest = { version = "0.10.1", optional = true }
|
||||
protobuf = "2.20"
|
||||
rand_core = "0.5.1"
|
||||
subtle = "2.2.3"
|
||||
time = "0.2"
|
||||
zcash_note_encryption = { version = "0.0", path = "../components/zcash_note_encryption" }
|
||||
zcash_primitives = { version = "0.5", path = "../zcash_primitives" }
|
||||
proptest = { version = "0.10.1", optional = true }
|
||||
percent-encoding = "2.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen-pure = "2.20"
|
||||
|
|
|
@ -49,29 +49,21 @@ pub fn decrypt_transaction<P: consensus::Parameters>(
|
|||
let ovk = extfvk.fvk.ovk;
|
||||
|
||||
for (index, output) in tx.shielded_outputs.iter().enumerate() {
|
||||
let ((note, to, memo), outgoing) = match try_sapling_note_decryption(
|
||||
params,
|
||||
height,
|
||||
&ivk,
|
||||
&output.ephemeral_key,
|
||||
&output.cmu,
|
||||
&output.enc_ciphertext,
|
||||
) {
|
||||
Some(ret) => (ret, false),
|
||||
None => match try_sapling_output_recovery(
|
||||
params,
|
||||
height,
|
||||
&ovk,
|
||||
&output.cv,
|
||||
&output.cmu,
|
||||
&output.ephemeral_key,
|
||||
&output.enc_ciphertext,
|
||||
&output.out_ciphertext,
|
||||
) {
|
||||
Some(ret) => (ret, true),
|
||||
None => continue,
|
||||
},
|
||||
};
|
||||
let ((note, to, memo), outgoing) =
|
||||
match try_sapling_note_decryption(params, height, &ivk, output) {
|
||||
Some(ret) => (ret, false),
|
||||
None => match try_sapling_output_recovery(
|
||||
params,
|
||||
height,
|
||||
&ovk,
|
||||
&output.cv,
|
||||
output,
|
||||
&output.out_ciphertext,
|
||||
) {
|
||||
Some(ret) => (ret, true),
|
||||
None => continue,
|
||||
},
|
||||
};
|
||||
decrypted.push(DecryptedOutput {
|
||||
index,
|
||||
note,
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
|
||||
use ff::PrimeField;
|
||||
use group::GroupEncoding;
|
||||
use std::convert::TryInto;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use zcash_primitives::{
|
||||
block::{BlockHash, BlockHeader},
|
||||
consensus::BlockHeight,
|
||||
sapling::Nullifier,
|
||||
transaction::components::sapling::{CompactOutputDescription, OutputDescription},
|
||||
};
|
||||
|
||||
use zcash_note_encryption::COMPACT_NOTE_SIZE;
|
||||
|
||||
pub mod compact_formats;
|
||||
|
||||
impl compact_formats::CompactBlock {
|
||||
|
@ -98,6 +101,28 @@ impl compact_formats::CompactOutput {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<OutputDescription> for compact_formats::CompactOutput {
|
||||
fn from(out: OutputDescription) -> compact_formats::CompactOutput {
|
||||
let mut result = compact_formats::CompactOutput::new();
|
||||
result.set_cmu(out.cmu.to_repr().to_vec());
|
||||
result.set_epk(out.ephemeral_key.to_bytes().to_vec());
|
||||
result.set_ciphertext(out.enc_ciphertext[..COMPACT_NOTE_SIZE].to_vec());
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<compact_formats::CompactOutput> for CompactOutputDescription {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: compact_formats::CompactOutput) -> Result<Self, Self::Error> {
|
||||
Ok(CompactOutputDescription {
|
||||
cmu: value.cmu()?,
|
||||
epk: value.epk()?,
|
||||
enc_ciphertext: value.ciphertext,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl compact_formats::CompactSpend {
|
||||
pub fn nf(&self) -> Result<Nullifier, ()> {
|
||||
Nullifier::from_slice(&self.nf).map_err(|_| ())
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
|
||||
use ff::PrimeField;
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
use zcash_primitives::{
|
||||
consensus::{self, BlockHeight},
|
||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
||||
sapling::{
|
||||
note_encryption::try_sapling_compact_note_decryption, Node, Note, Nullifier,
|
||||
PaymentAddress, SaplingIvk,
|
||||
note_encryption::{try_sapling_compact_note_decryption, SaplingShieldedOutput},
|
||||
Node, Note, Nullifier, PaymentAddress, SaplingIvk,
|
||||
},
|
||||
transaction::TxId,
|
||||
transaction::{components::sapling::CompactOutputDescription, TxId},
|
||||
zip32::ExtendedFullViewingKey,
|
||||
};
|
||||
|
||||
|
@ -38,12 +39,10 @@ fn scan_output<P: consensus::Parameters, K: ScanningKey>(
|
|||
block_witnesses: &mut [&mut IncrementalWitness<Node>],
|
||||
new_witnesses: &mut [&mut IncrementalWitness<Node>],
|
||||
) -> Option<WalletShieldedOutput<K::Nf>> {
|
||||
let cmu = output.cmu().ok()?;
|
||||
let epk = output.epk().ok()?;
|
||||
let ct = output.ciphertext;
|
||||
let output = CompactOutputDescription::try_from(output).ok()?;
|
||||
|
||||
// Increment tree and witnesses
|
||||
let node = Node::new(cmu.to_repr());
|
||||
let node = Node::new(output.cmu.to_repr());
|
||||
for witness in existing_witnesses {
|
||||
witness.append(node).unwrap();
|
||||
}
|
||||
|
@ -56,7 +55,7 @@ fn scan_output<P: consensus::Parameters, K: ScanningKey>(
|
|||
tree.append(node).unwrap();
|
||||
|
||||
for (account, vk) in vks.iter() {
|
||||
let (note, to) = match vk.try_decryption(params, height, &epk, &cmu, &ct) {
|
||||
let (note, to) = match vk.try_decryption(params, height, &output) {
|
||||
Some(ret) => ret,
|
||||
None => continue,
|
||||
};
|
||||
|
@ -74,8 +73,8 @@ fn scan_output<P: consensus::Parameters, K: ScanningKey>(
|
|||
|
||||
return Some(WalletShieldedOutput {
|
||||
index,
|
||||
cmu,
|
||||
epk,
|
||||
cmu: output.cmu,
|
||||
epk: output.epk,
|
||||
account: **account,
|
||||
note,
|
||||
to,
|
||||
|
@ -108,13 +107,11 @@ 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>(
|
||||
fn try_decryption<P: consensus::Parameters, Output: SaplingShieldedOutput<P>>(
|
||||
&self,
|
||||
params: &P,
|
||||
height: BlockHeight,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
ct: &[u8],
|
||||
output: &Output,
|
||||
) -> Option<(Note, PaymentAddress)>;
|
||||
|
||||
/// Produces the nullifier for the specified note and witness, if possible.
|
||||
|
@ -132,15 +129,13 @@ pub trait ScanningKey {
|
|||
impl ScanningKey for ExtendedFullViewingKey {
|
||||
type Nf = Nullifier;
|
||||
|
||||
fn try_decryption<P: consensus::Parameters>(
|
||||
fn try_decryption<P: consensus::Parameters, Output: SaplingShieldedOutput<P>>(
|
||||
&self,
|
||||
params: &P,
|
||||
height: BlockHeight,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
ct: &[u8],
|
||||
output: &Output,
|
||||
) -> Option<(Note, PaymentAddress)> {
|
||||
try_sapling_compact_note_decryption(params, height, &self.fvk.vk.ivk(), &epk, &cmu, &ct)
|
||||
try_sapling_compact_note_decryption(params, height, &self.fvk.vk.ivk(), output)
|
||||
}
|
||||
|
||||
fn nf(&self, note: &Note, witness: &IncrementalWitness<Node>) -> Self::Nf {
|
||||
|
@ -155,15 +150,13 @@ impl ScanningKey for ExtendedFullViewingKey {
|
|||
impl ScanningKey for SaplingIvk {
|
||||
type Nf = ();
|
||||
|
||||
fn try_decryption<P: consensus::Parameters>(
|
||||
fn try_decryption<P: consensus::Parameters, Output: SaplingShieldedOutput<P>>(
|
||||
&self,
|
||||
params: &P,
|
||||
height: BlockHeight,
|
||||
epk: &jubjub::ExtendedPoint,
|
||||
cmu: &bls12_381::Scalar,
|
||||
ct: &[u8],
|
||||
output: &Output,
|
||||
) -> Option<(Note, PaymentAddress)> {
|
||||
try_sapling_compact_note_decryption(params, height, self, &epk, &cmu, &ct)
|
||||
try_sapling_compact_note_decryption(params, height, self, output)
|
||||
}
|
||||
|
||||
fn nf(&self, _note: &Note, _witness: &IncrementalWitness<Node>) {}
|
||||
|
|
|
@ -635,9 +635,7 @@ mod tests {
|
|||
sapling_activation_height(),
|
||||
&extfvk.fvk.ovk,
|
||||
&output.cv,
|
||||
&output.cmu,
|
||||
&output.ephemeral_key,
|
||||
&output.enc_ciphertext,
|
||||
output,
|
||||
&output.out_ciphertext,
|
||||
)
|
||||
};
|
||||
|
|
|
@ -57,30 +57,11 @@ fn bench_note_decryption(c: &mut Criterion) {
|
|||
let mut group = c.benchmark_group("Sapling note decryption");
|
||||
|
||||
group.bench_function("valid", |b| {
|
||||
b.iter(|| {
|
||||
try_sapling_note_decryption(
|
||||
&TEST_NETWORK,
|
||||
height,
|
||||
&valid_ivk,
|
||||
&output.ephemeral_key,
|
||||
&output.cmu,
|
||||
&output.enc_ciphertext,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
b.iter(|| try_sapling_note_decryption(&TEST_NETWORK, height, &valid_ivk, &output).unwrap())
|
||||
});
|
||||
|
||||
group.bench_function("invalid", |b| {
|
||||
b.iter(|| {
|
||||
try_sapling_note_decryption(
|
||||
&TEST_NETWORK,
|
||||
height,
|
||||
&invalid_ivk,
|
||||
&output.ephemeral_key,
|
||||
&output.cmu,
|
||||
&output.enc_ciphertext,
|
||||
)
|
||||
})
|
||||
b.iter(|| try_sapling_note_decryption(&TEST_NETWORK, height, &invalid_ivk, &output))
|
||||
});
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,11 +3,19 @@ use group::GroupEncoding;
|
|||
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::sapling::{
|
||||
redjubjub::{PublicKey, Signature},
|
||||
Nullifier,
|
||||
use zcash_note_encryption::ShieldedOutput;
|
||||
|
||||
use crate::{
|
||||
consensus,
|
||||
sapling::{
|
||||
note_encryption::{SaplingDomain, SaplingShieldedOutput},
|
||||
redjubjub::{PublicKey, Signature},
|
||||
Nullifier,
|
||||
},
|
||||
};
|
||||
|
||||
use zcash_note_encryption::COMPACT_NOTE_SIZE;
|
||||
|
||||
use super::GROTH_PROOF_SIZE;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -110,6 +118,26 @@ pub struct OutputDescription {
|
|||
pub zkproof: [u8; GROTH_PROOF_SIZE],
|
||||
}
|
||||
|
||||
impl<P: consensus::Parameters> ShieldedOutput<SaplingDomain<P>> for OutputDescription {
|
||||
fn epk(&self) -> &jubjub::ExtendedPoint {
|
||||
&self.ephemeral_key
|
||||
}
|
||||
|
||||
fn cmstar(&self) -> [u8; 32] {
|
||||
self.cmu.to_repr()
|
||||
}
|
||||
|
||||
fn enc_ciphertext(&self) -> &[u8] {
|
||||
&self.enc_ciphertext
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: consensus::Parameters> SaplingShieldedOutput<P> for OutputDescription {
|
||||
fn cmu(&self) -> &bls12_381::Scalar {
|
||||
&self.cmu
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for OutputDescription {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
|
@ -191,3 +219,39 @@ impl OutputDescription {
|
|||
writer.write_all(&self.zkproof)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompactOutputDescription {
|
||||
pub epk: jubjub::ExtendedPoint,
|
||||
pub cmu: bls12_381::Scalar,
|
||||
pub enc_ciphertext: Vec<u8>,
|
||||
}
|
||||
|
||||
impl From<OutputDescription> for CompactOutputDescription {
|
||||
fn from(out: OutputDescription) -> CompactOutputDescription {
|
||||
CompactOutputDescription {
|
||||
epk: out.ephemeral_key,
|
||||
cmu: out.cmu,
|
||||
enc_ciphertext: out.enc_ciphertext[..COMPACT_NOTE_SIZE].to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: consensus::Parameters> ShieldedOutput<SaplingDomain<P>> for CompactOutputDescription {
|
||||
fn epk(&self) -> &jubjub::ExtendedPoint {
|
||||
&self.epk
|
||||
}
|
||||
|
||||
fn cmstar(&self) -> [u8; 32] {
|
||||
self.cmu.to_repr()
|
||||
}
|
||||
|
||||
fn enc_ciphertext(&self) -> &[u8] {
|
||||
&self.enc_ciphertext
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: consensus::Parameters> SaplingShieldedOutput<P> for CompactOutputDescription {
|
||||
fn cmu(&self) -> &bls12_381::Scalar {
|
||||
&self.cmu
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue