Skip to content

Commit

Permalink
Connect swaps/swap claims (#4035)
Browse files Browse the repository at this point in the history
  • Loading branch information
jessepinho authored Mar 22, 2024
1 parent a2b1cc3 commit d231a61
Show file tree
Hide file tree
Showing 9 changed files with 538 additions and 23 deletions.
6 changes: 2 additions & 4 deletions crates/bin/pcli/src/transaction_view_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,7 @@ impl TransactionViewExt for TransactionView {
penumbra_transaction::ActionView::Swap(swap) => {
// Typical swaps are one asset for another, but we can't know that for sure.
match swap {
SwapView::Visible {
swap: _,
swap_plaintext,
} => {
SwapView::Visible { swap_plaintext, .. } => {
let (from_asset, from_value, to_asset) = match (
swap_plaintext.delta_1_i.value(),
swap_plaintext.delta_2_i.value(),
Expand Down Expand Up @@ -273,6 +270,7 @@ impl TransactionViewExt for TransactionView {
swap_claim,
output_1,
output_2,
swap_tx: _,
} => {
// View service can't see SwapClaims: https://github.com/penumbra-zone/penumbra/issues/2547
dbg!(swap_claim);
Expand Down
45 changes: 33 additions & 12 deletions crates/core/component/dex/src/swap/view.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use penumbra_asset::asset::Metadata;
use penumbra_proto::{penumbra::core::component::dex::v1 as pb, DomainType};
use penumbra_shielded_pool::NoteView;
use penumbra_txhash::TransactionId;
use serde::{Deserialize, Serialize};

use crate::BatchSwapOutputData;

use super::{Swap, SwapPlaintext};

#[derive(Clone, Debug, Serialize, Deserialize)]
Expand All @@ -10,6 +15,12 @@ pub enum SwapView {
Visible {
swap: Swap,
swap_plaintext: SwapPlaintext,
output_1: Option<NoteView>,
output_2: Option<NoteView>,
claim_tx: Option<TransactionId>,
asset_1_metadata: Option<Metadata>,
asset_2_metadata: Option<Metadata>,
batch_swap_output_data: Option<BatchSwapOutputData>,
},
Opaque {
swap: Swap,
Expand Down Expand Up @@ -37,6 +48,15 @@ impl TryFrom<pb::SwapView> for SwapView {
.swap_plaintext
.ok_or_else(|| anyhow::anyhow!("missing swap plaintext field"))?
.try_into()?,
output_1: x.output_1.map(TryInto::try_into).transpose()?,
output_2: x.output_2.map(TryInto::try_into).transpose()?,
claim_tx: x.claim_tx.map(TryInto::try_into).transpose()?,
asset_1_metadata: x.asset_1_metadata.map(TryInto::try_into).transpose()?,
asset_2_metadata: x.asset_2_metadata.map(TryInto::try_into).transpose()?,
batch_swap_output_data: x
.batch_swap_output_data
.map(TryInto::try_into)
.transpose()?,
}),
pb::swap_view::SwapView::Opaque(x) => Ok(SwapView::Opaque {
swap: x
Expand All @@ -55,18 +75,22 @@ impl From<SwapView> for pb::SwapView {
SwapView::Visible {
swap,
swap_plaintext,
output_1,
output_2,
claim_tx,
asset_1_metadata,
asset_2_metadata,
batch_swap_output_data,
} => Self {
swap_view: Some(sv::SwapView::Visible(sv::Visible {
swap: Some(swap.into()),
swap_plaintext: Some(swap_plaintext.into()),
// Swap claim crossreferencing is not yet supported in the Rust stack.
claim_tx: None,
// These fields are also not yet supported in the Rust stack.
asset_1_metadata: None,
asset_2_metadata: None,
batch_swap_output_data: None,
output_1: None,
output_2: None,
output_1: output_1.map(Into::into),
output_2: output_2.map(Into::into),
claim_tx: claim_tx.map(Into::into),
asset_1_metadata: asset_1_metadata.map(Into::into),
asset_2_metadata: asset_2_metadata.map(Into::into),
batch_swap_output_data: batch_swap_output_data.map(Into::into),
})),
},
SwapView::Opaque { swap } => Self {
Expand All @@ -81,10 +105,7 @@ impl From<SwapView> for pb::SwapView {
impl From<SwapView> for Swap {
fn from(v: SwapView) -> Self {
match v {
SwapView::Visible {
swap,
swap_plaintext: _,
} => swap,
SwapView::Visible { swap, .. } => swap,
SwapView::Opaque { swap } => swap,
}
}
Expand Down
8 changes: 6 additions & 2 deletions crates/core/component/dex/src/swap_claim/view.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use penumbra_proto::{penumbra::core::component::dex::v1 as pbd, DomainType};
use penumbra_shielded_pool::NoteView;
use penumbra_txhash::TransactionId;
use serde::{Deserialize, Serialize};

use super::SwapClaim;
Expand All @@ -12,6 +13,7 @@ pub enum SwapClaimView {
swap_claim: SwapClaim,
output_1: NoteView,
output_2: NoteView,
swap_tx: Option<TransactionId>,
},
Opaque {
swap_claim: SwapClaim,
Expand Down Expand Up @@ -43,6 +45,7 @@ impl TryFrom<pbd::SwapClaimView> for SwapClaimView {
.output_2
.ok_or_else(|| anyhow::anyhow!("missing output_2 field"))?
.try_into()?,
swap_tx: x.swap_tx.map(TryInto::try_into).transpose()?,
}),
pbd::swap_claim_view::SwapClaimView::Opaque(x) => Ok(SwapClaimView::Opaque {
swap_claim: x
Expand All @@ -62,13 +65,13 @@ impl From<SwapClaimView> for pbd::SwapClaimView {
swap_claim,
output_1,
output_2,
swap_tx,
} => Self {
swap_claim_view: Some(scv::SwapClaimView::Visible(scv::Visible {
swap_claim: Some(swap_claim.into()),
output_1: Some(output_1.into()),
output_2: Some(output_2.into()),
// Swap claim crossreferencing is not yet supported in the Rust stack.
swap_tx: None,
swap_tx: swap_tx.map(Into::into),
})),
},
SwapClaimView::Opaque { swap_claim } => Self {
Expand All @@ -87,6 +90,7 @@ impl From<SwapClaimView> for SwapClaim {
swap_claim,
output_1: _,
output_2: _,
swap_tx: _,
} => swap_claim,
SwapClaimView::Opaque { swap_claim } => swap_claim,
}
Expand Down
46 changes: 42 additions & 4 deletions crates/core/transaction/src/is_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,44 @@ impl IsAction for Swap {
});

ActionView::Swap(match plaintext {
Some(swap_plaintext) => SwapView::Visible {
swap: self.to_owned(),
swap_plaintext,
},
Some(swap_plaintext) => {
// If we can find a matching BSOD in the TxP, use it to compute the output notes
// for the swap.
let bsod = txp
.batch_swap_output_data
.iter()
// This finds the first matching one; there should only be one
// per trading pair per block and we trust the TxP provider not to lie about it.
.find(|bsod| bsod.trading_pair == swap_plaintext.trading_pair);

let (output_1, output_2) = match bsod.map(|bsod| swap_plaintext.output_notes(bsod))
{
Some((output_1, output_2)) => {
(Some(txp.view_note(output_1)), Some(txp.view_note(output_2)))
}
None => (None, None),
};

SwapView::Visible {
swap: self.to_owned(),
swap_plaintext: swap_plaintext.clone(),
output_1,
output_2,
claim_tx: txp
.nullification_transaction_ids_by_commitment
.get(&commitment)
.cloned(),
batch_swap_output_data: bsod.cloned(),
asset_1_metadata: txp
.denoms
.get(&swap_plaintext.trading_pair.asset_1())
.cloned(),
asset_2_metadata: txp
.denoms
.get(&swap_plaintext.trading_pair.asset_2())
.cloned(),
}
}
None => SwapView::Opaque {
swap: self.to_owned(),
},
Expand All @@ -356,6 +390,10 @@ impl IsAction for SwapClaim {
swap_claim: self.to_owned(),
output_1: txp.view_note(output_1.to_owned()),
output_2: txp.view_note(output_2.to_owned()),
swap_tx: txp
.creation_transaction_ids_by_nullifier
.get(&self.body.nullifier)
.cloned(),
};
ActionView::SwapClaim(swap_claim_view)
}
Expand Down
86 changes: 85 additions & 1 deletion crates/core/transaction/src/view/transaction_perspective.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::anyhow;
use pbjson_types::Any;
use penumbra_asset::{asset, EstimatedPrice, Value, ValueView};
use penumbra_dex::BatchSwapOutputData;
use penumbra_keys::{Address, AddressView, PayloadKey};
use penumbra_proto::core::transaction::v1::{
self as pb, NullifierWithNote, PayloadKeyWithCommitment,
Expand Down Expand Up @@ -44,6 +45,18 @@ pub struct TransactionPerspective {
pub prices: Vec<EstimatedPrice>,
/// Any relevant extended metadata.
pub extended_metadata: BTreeMap<asset::Id, Any>,
/// Associates nullifiers with the transaction IDs that created the state commitments.
///
/// Allows walking backwards from a spend to the transaction that created the note.
pub creation_transaction_ids_by_nullifier: BTreeMap<Nullifier, TransactionId>,
/// Associates commitments with the transaction IDs that eventually nullified them.
///
/// Allows walking forwards from an output to the transaction that later spent it.
pub nullification_transaction_ids_by_commitment: BTreeMap<note::StateCommitment, TransactionId>,
/// Any relevant batch swap output data.
///
/// This can be used to fill in information about swap outputs.
pub batch_swap_output_data: Vec<BatchSwapOutputData>,
}

impl TransactionPerspective {
Expand All @@ -68,6 +81,13 @@ impl TransactionPerspective {
None => AddressView::Opaque { address },
}
}

pub fn get_and_view_advice_note(&self, commitment: &note::StateCommitment) -> Option<NoteView> {
self.advice_notes
.get(commitment)
.cloned()
.map(|note| self.view_note(note))
}
}

impl TransactionPerspective {}
Expand Down Expand Up @@ -119,6 +139,31 @@ impl From<TransactionPerspective> for pb::TransactionPerspective {
extended_metadata: Some(v),
})
.collect(),
creation_transaction_ids_by_nullifier: msg
.creation_transaction_ids_by_nullifier
.into_iter()
.map(
|(k, v)| pb::transaction_perspective::CreationTransactionIdByNullifier {
nullifier: Some(k.into()),
transaction_id: Some(v.into()),
},
)
.collect(),
nullification_transaction_ids_by_commitment: msg
.nullification_transaction_ids_by_commitment
.into_iter()
.map(
|(k, v)| pb::transaction_perspective::NullificationTransactionIdByCommitment {
commitment: Some(k.into()),
transaction_id: Some(v.into()),
},
)
.collect(),
batch_swap_output_data: msg
.batch_swap_output_data
.into_iter()
.map(Into::into)
.collect(),
}
}
}
Expand Down Expand Up @@ -177,7 +222,7 @@ impl TryFrom<pb::TransactionPerspective> for TransactionPerspective {
);
}

let transaction_id: penumbra_txhash::TransactionId = match msg.transaction_id {
let transaction_id: TransactionId = match msg.transaction_id {
Some(tx_id) => tx_id.try_into()?,
None => TransactionId::default(),
};
Expand Down Expand Up @@ -207,6 +252,45 @@ impl TryFrom<pb::TransactionPerspective> for TransactionPerspective {
))
})
.collect::<Result<_, anyhow::Error>>()?,
creation_transaction_ids_by_nullifier: msg
.creation_transaction_ids_by_nullifier
.into_iter()
.map(|ct| {
Ok((
ct.nullifier
.ok_or_else(|| anyhow!("missing nullifier in creation transaction ID"))?
.try_into()?,
ct.transaction_id
.ok_or_else(|| {
anyhow!("missing transaction ID in creation transaction ID")
})?
.try_into()?,
))
})
.collect::<Result<_, anyhow::Error>>()?,
nullification_transaction_ids_by_commitment: msg
.nullification_transaction_ids_by_commitment
.into_iter()
.map(|nt| {
Ok((
nt.commitment
.ok_or_else(|| {
anyhow!("missing commitment in nullification transaction ID")
})?
.try_into()?,
nt.transaction_id
.ok_or_else(|| {
anyhow!("missing transaction ID in nullification transaction ID")
})?
.try_into()?,
))
})
.collect::<Result<_, anyhow::Error>>()?,
batch_swap_output_data: msg
.batch_swap_output_data
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?,
})
}
}
Loading

0 comments on commit d231a61

Please sign in to comment.