penumbra_sdk_shielded_pool/
note_payload.rs1use anyhow::{Context, Error};
2
3use penumbra_sdk_keys::keys::FullViewingKey;
4use penumbra_sdk_num::Amount;
5use penumbra_sdk_proto::{penumbra::core::component::shielded_pool::v1 as pb, DomainType};
6use serde::{Deserialize, Serialize};
7
8use crate::{note, Note, NoteCiphertext};
9use decaf377_ka as ka;
10
11#[derive(Clone, Serialize, Deserialize)]
12#[serde(try_from = "pb::NotePayload", into = "pb::NotePayload")]
13pub struct NotePayload {
14 pub note_commitment: note::StateCommitment,
15 pub ephemeral_key: ka::Public,
16 pub encrypted_note: NoteCiphertext,
17}
18
19impl NotePayload {
20 pub fn trial_decrypt(&self, fvk: &FullViewingKey) -> Option<Note> {
21 let note = Note::decrypt(&self.encrypted_note, fvk.incoming(), &self.ephemeral_key).ok()?;
24 tracing::debug!(note_commitment = ?note.commit(), ?note, "found note while scanning");
25
26 if note.amount() == Amount::zero() {
29 tracing::debug!("ignoring note recording zero assets");
31 return None;
32 }
33 if !note.controlled_by(fvk) {
35 tracing::warn!("decrypted note that is not spendable by provided full viewing key");
38 return None;
39 }
40 if note.commit() != self.note_commitment {
42 tracing::warn!("decrypted note does not match provided note commitment");
45 return None;
46 }
47
48 Some(note)
58 }
59}
60
61impl std::fmt::Debug for NotePayload {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("NotePayload")
64 .field("note_commitment", &self.note_commitment)
65 .field("ephemeral_key", &self.ephemeral_key)
66 .field("encrypted_note", &"...")
67 .finish()
68 }
69}
70
71impl DomainType for NotePayload {
72 type Proto = pb::NotePayload;
73}
74
75impl From<NotePayload> for pb::NotePayload {
76 fn from(msg: NotePayload) -> Self {
77 pb::NotePayload {
78 note_commitment: Some(msg.note_commitment.into()),
79 ephemeral_key: msg.ephemeral_key.0.to_vec(),
80 encrypted_note: Some(msg.encrypted_note.into()),
81 }
82 }
83}
84
85impl TryFrom<pb::NotePayload> for NotePayload {
86 type Error = Error;
87
88 fn try_from(proto: pb::NotePayload) -> anyhow::Result<Self, Self::Error> {
89 Ok(NotePayload {
90 note_commitment: proto
91 .note_commitment
92 .ok_or_else(|| anyhow::anyhow!("missing note commitment"))?
93 .try_into()?,
94 ephemeral_key: ka::Public::try_from(&proto.ephemeral_key[..])
95 .context("ephemeral key malformed")?,
96 encrypted_note: proto
97 .encrypted_note
98 .ok_or_else(|| anyhow::anyhow!("missing encrypted note"))?
99 .try_into()?,
100 })
101 }
102}