Skip to content

Commit

Permalink
add view struct for memos with AddressView for sender
Browse files Browse the repository at this point in the history
  • Loading branch information
redshiftzero committed Nov 15, 2023
1 parent 11d3b52 commit 3bef4f9
Show file tree
Hide file tree
Showing 13 changed files with 488 additions and 228 deletions.
2 changes: 1 addition & 1 deletion crates/bin/pcli/src/command/view/transaction_hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl TransactionHashesCmd {
for tx_info in txs {
let (return_address, memo) = match tx_info.view.body_view.memo_view {
Some(MemoView::Visible { plaintext, .. }) => (
plaintext.return_address.display_short_form(),
plaintext.return_address.address().display_short_form(),
plaintext.text,
),
_ => (String::new(), String::new()),
Expand Down
2 changes: 1 addition & 1 deletion crates/bin/pcli/src/command/view/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ impl TxCmd {
} => {
metadata_table.add_row(vec![
"Transaction Memo Sender",
&plaintext.return_address.to_string(),
&plaintext.return_address.address().to_string(),
]);
metadata_table.add_row(vec!["Transaction Memo Text", &plaintext.text]);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/core/transaction/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub use error::Error;
pub use id::Id;
pub use is_action::IsAction;
pub use transaction::{Transaction, TransactionBody, TransactionParameters};
pub use view::{ActionView, MemoView, TransactionPerspective, TransactionView};
pub use view::{ActionView, MemoPlaintextView, MemoView, TransactionPerspective, TransactionView};
pub use witness_data::WitnessData;

/// A compatibility wrapper for trait implementations that are temporarily duplicated
Expand Down
16 changes: 11 additions & 5 deletions crates/core/transaction/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use serde::{Deserialize, Serialize};
use crate::{
memo::{MemoCiphertext, MemoPlaintext},
view::{action_view::OutputView, MemoView, TransactionBodyView},
Action, ActionView, Id, IsAction, TransactionPerspective, TransactionView,
Action, ActionView, Id, IsAction, MemoPlaintextView, TransactionPerspective, TransactionView,
};

#[derive(Clone, Debug, Default)]
Expand Down Expand Up @@ -252,10 +252,16 @@ impl Transaction {

let memo_view = match memo_ciphertext {
Some(ciphertext) => match memo_plaintext {
Some(plaintext) => Some(MemoView::Visible {
plaintext,
ciphertext,
}),
Some(plaintext) => {
let plaintext_view: MemoPlaintextView = MemoPlaintextView {
return_address: txp.view_address(plaintext.return_address),
text: plaintext.text,
};
Some(MemoView::Visible {
plaintext: plaintext_view,
ciphertext,
})
}
None => Some(MemoView::Opaque { ciphertext }),
},
None => None,
Expand Down
40 changes: 38 additions & 2 deletions crates/core/transaction/src/view.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::Context;
use decaf377_rdsa::{Binding, Signature};
use penumbra_fee::Fee;
use penumbra_keys::AddressView;
use penumbra_proto::{core::transaction::v1alpha1 as pbt, DomainType, TypeUrl};

use serde::{Deserialize, Serialize};
Expand All @@ -13,7 +14,7 @@ use penumbra_tct as tct;
pub use transaction_perspective::TransactionPerspective;

use crate::{
memo::{MemoCiphertext, MemoPlaintext},
memo::MemoCiphertext,
transaction::{DetectionData, TransactionParameters},
Action, Transaction, TransactionBody,
};
Expand Down Expand Up @@ -44,14 +45,21 @@ pub struct TransactionBodyView {
#[allow(clippy::large_enum_variant)]
pub enum MemoView {
Visible {
plaintext: MemoPlaintext,
plaintext: MemoPlaintextView,
ciphertext: MemoCiphertext,
},
Opaque {
ciphertext: MemoCiphertext,
},
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(try_from = "pbt::MemoPlaintextView", into = "pbt::MemoPlaintextView")]
pub struct MemoPlaintextView {
pub return_address: AddressView,
pub text: String,
}

impl TransactionView {
pub fn transaction(&self) -> Transaction {
let mut actions = Vec::new();
Expand Down Expand Up @@ -274,3 +282,31 @@ impl TryFrom<pbt::MemoView> for MemoView {
}
}
}

impl From<MemoPlaintextView> for pbt::MemoPlaintextView {
fn from(v: MemoPlaintextView) -> Self {
Self {
return_address: Some(v.return_address.into()),
text: v.text,
}
}
}

impl TryFrom<pbt::MemoPlaintextView> for MemoPlaintextView {
type Error = anyhow::Error;

fn try_from(v: pbt::MemoPlaintextView) -> Result<Self, Self::Error> {
let sender: AddressView = v
.return_address
.ok_or_else(|| anyhow::anyhow!("memo plan missing memo plaintext"))?
.try_into()
.context("return address malformed")?;

let text: String = v.text;

Ok(Self {
return_address: sender,
text,
})
}
}
9 changes: 8 additions & 1 deletion crates/core/transaction/src/view/transaction_perspective.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::anyhow;
use penumbra_asset::asset;
use penumbra_keys::{AddressView, PayloadKey};
use penumbra_keys::{Address, AddressView, PayloadKey};
use penumbra_proto::core::transaction::v1alpha1::{
self as pb, NullifierWithNote, PayloadKeyWithCommitment,
};
Expand Down Expand Up @@ -66,6 +66,13 @@ impl TransactionPerspective {
rseed: note.rseed(),
}
}

pub fn view_address(&self, address: Address) -> AddressView {
match self.address_views.iter().find(|av| av.address() == address) {
Some(av) => av.clone(),
None => AddressView::Opaque { address },
}
}
}

impl TransactionPerspective {}
Expand Down
12 changes: 11 additions & 1 deletion crates/proto/src/gen/penumbra.core.transaction.v1alpha1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,16 @@ pub struct MemoPlaintext {
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct MemoPlaintextView {
#[prost(message, optional, tag = "1")]
pub return_address: ::core::option::Option<
super::super::keys::v1alpha1::AddressView,
>,
#[prost(string, tag = "2")]
pub text: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct MemoView {
#[prost(oneof = "memo_view::MemoView", tags = "1, 2")]
pub memo_view: ::core::option::Option<memo_view::MemoView>,
Expand All @@ -536,7 +546,7 @@ pub mod memo_view {
#[prost(message, optional, tag = "1")]
pub ciphertext: ::core::option::Option<super::MemoCiphertext>,
#[prost(message, optional, tag = "2")]
pub plaintext: ::core::option::Option<super::MemoPlaintext>,
pub plaintext: ::core::option::Option<super::MemoPlaintextView>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
Expand Down
109 changes: 109 additions & 0 deletions crates/proto/src/gen/penumbra.core.transaction.v1alpha1.serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1899,6 +1899,115 @@ impl<'de> serde::Deserialize<'de> for MemoPlaintext {
deserializer.deserialize_struct("penumbra.core.transaction.v1alpha1.MemoPlaintext", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for MemoPlaintextView {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if self.return_address.is_some() {
len += 1;
}
if !self.text.is_empty() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("penumbra.core.transaction.v1alpha1.MemoPlaintextView", len)?;
if let Some(v) = self.return_address.as_ref() {
struct_ser.serialize_field("returnAddress", v)?;
}
if !self.text.is_empty() {
struct_ser.serialize_field("text", &self.text)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for MemoPlaintextView {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &[
"return_address",
"returnAddress",
"text",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
ReturnAddress,
Text,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct GeneratedVisitor;

impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = GeneratedField;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}

#[allow(unused_variables)]
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"returnAddress" | "return_address" => Ok(GeneratedField::ReturnAddress),
"text" => Ok(GeneratedField::Text),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = MemoPlaintextView;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct penumbra.core.transaction.v1alpha1.MemoPlaintextView")
}

fn visit_map<V>(self, mut map_: V) -> std::result::Result<MemoPlaintextView, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut return_address__ = None;
let mut text__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::ReturnAddress => {
if return_address__.is_some() {
return Err(serde::de::Error::duplicate_field("returnAddress"));
}
return_address__ = map_.next_value()?;
}
GeneratedField::Text => {
if text__.is_some() {
return Err(serde::de::Error::duplicate_field("text"));
}
text__ = Some(map_.next_value()?);
}
}
}
Ok(MemoPlaintextView {
return_address: return_address__,
text: text__.unwrap_or_default(),
})
}
}
deserializer.deserialize_struct("penumbra.core.transaction.v1alpha1.MemoPlaintextView", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for MemoPlan {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
Expand Down
Binary file modified crates/proto/src/gen/proto_descriptor.bin.no_lfs
Binary file not shown.
6 changes: 6 additions & 0 deletions crates/view/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,12 @@ impl ViewProtocolService for ViewService {
let address = note.address();
address_views.insert(address, fvk.view_address(address));
asset_ids.insert(note.asset_id());

// Also add an AddressView for the sender address in the memo.
let memo = tx.decrypt_memo(&fvk).map_err(|_| {
tonic::Status::internal("Error decrypting memo for OutputView")
})?;
address_views.insert(memo.return_address, fvk.view_address(address));
}
ActionView::Swap(SwapView::Visible { swap_plaintext, .. }) => {
let address = swap_plaintext.claim_address;
Expand Down
4 changes: 4 additions & 0 deletions crates/wasm/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ pub async fn transaction_info_inner(
let address = note.address();
address_views.insert(address, fvk.view_address(address));
asset_ids.insert(note.asset_id());

// Also add an AddressView for the sender address in the memo.
let memo = tx.decrypt_memo(&fvk)?;
address_views.insert(memo.return_address, fvk.view_address(address));
}
ActionView::Swap(SwapView::Visible { swap_plaintext, .. }) => {
let address = swap_plaintext.claim_address;
Expand Down
Loading

0 comments on commit 3bef4f9

Please sign in to comment.