Skip to content

Commit

Permalink
transaction: implement effects summary (#4943)
Browse files Browse the repository at this point in the history
## Describe your changes

References #4939

## Checklist before requesting a review

- [x] I have added guiding text to explain how a reviewer should test
these changes.

- [x] If this code contains consensus-breaking changes, I have added the
"consensus-breaking" label. Otherwise, I declare my belief that there
are not consensus-breaking changes, for the following reason:

---------

Signed-off-by: Erwan Or <[email protected]>
Co-authored-by: Lucas Meier <[email protected]>
Co-authored-by: Erwan Or <[email protected]>
Co-authored-by: Erwan Or <[email protected]>
  • Loading branch information
4 people authored Dec 13, 2024
1 parent 707192c commit c654809
Show file tree
Hide file tree
Showing 12 changed files with 1,606 additions and 12 deletions.
Binary file modified crates/cnidarium/src/gen/proto_descriptor.bin.no_lfs
Binary file not shown.
383 changes: 376 additions & 7 deletions crates/core/asset/src/balance.rs

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions crates/core/keys/src/address/view.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::cmp::Ordering;

use serde::{Deserialize, Serialize};

use penumbra_proto::{penumbra::core::keys::v1 as pb, DomainType};
Expand Down Expand Up @@ -99,6 +101,76 @@ impl TryFrom<pb::AddressView> for AddressView {
}
}

// Canonical ordering for serialization
impl PartialOrd for AddressView {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use AddressView::*;
// Opaque < Decoded
match (self, other) {
(Opaque { .. }, Decoded { .. }) => Some(Ordering::Less),
(Decoded { .. }, Opaque { .. }) => Some(Ordering::Greater),
(Opaque { address: a1 }, Opaque { address: a2 }) => a1.partial_cmp(a2),
(
Decoded {
address: a1,
index: i1,
wallet_id: w1,
},
Decoded {
address: a2,
index: i2,
wallet_id: w2,
},
) => (a1, i1, w1).partial_cmp(&(a2, i2, w2)),
}
}
}

impl Ord for AddressView {
fn cmp(&self, other: &Self) -> Ordering {
// Opaque < Decoded
match (self, other) {
(AddressView::Opaque { address: a1 }, AddressView::Opaque { address: a2 }) => {
a1.cmp(a2)
}
(
AddressView::Decoded {
address: a1,
index: i1,
wallet_id: w1,
},
AddressView::Decoded {
address: a2,
index: i2,
wallet_id: w2,
},
) => match a1.cmp(a2) {
Ordering::Equal => match i1.cmp(i2) {
Ordering::Equal => w1.cmp(w2),
ord => ord,
},
ord => ord,
},
(
AddressView::Opaque { address: _ },
AddressView::Decoded {
address: _,
index: _,
wallet_id: _,
},
) => Ordering::Less,
(
AddressView::Decoded {
address: _,
index: _,
wallet_id: _,
},
AddressView::Opaque { address: _ },
) => Ordering::Greater,
}
}
}

#[cfg(test)]
mod tests {
use rand_core::OsRng;
Expand Down
68 changes: 66 additions & 2 deletions crates/core/transaction/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ use anyhow::{Context, Error};
use ark_ff::Zero;
use decaf377::Fr;
use decaf377_rdsa::{Binding, Signature, VerificationKey, VerificationKeyBytes};
use penumbra_asset::Balance;
use penumbra_community_pool::{CommunityPoolDeposit, CommunityPoolOutput, CommunityPoolSpend};
use penumbra_dex::{
lp::action::{PositionClose, PositionOpen},
swap::Swap,
};
use penumbra_governance::{DelegatorVote, ProposalSubmit, ProposalWithdraw, ValidatorVote};
use penumbra_ibc::IbcRelay;
use penumbra_keys::{FullViewingKey, PayloadKey};
use penumbra_keys::{AddressView, FullViewingKey, PayloadKey};
use penumbra_proto::{
core::transaction::v1::{self as pbt},
DomainType, Message,
Expand Down Expand Up @@ -44,6 +45,20 @@ pub struct TransactionBody {
pub memo: Option<MemoCiphertext>,
}

/// Represents a transaction summary containing multiple effects.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(try_from = "pbt::TransactionSummary", into = "pbt::TransactionSummary")]
pub struct TransactionSummary {
pub effects: Vec<TransactionEffect>,
}

/// Represents an individual effect of a transaction.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TransactionEffect {
pub address: AddressView,
pub balance: Balance,
}

impl EffectingData for TransactionBody {
fn effect_hash(&self) -> EffectHash {
let mut state = blake2b_simd::Params::new()
Expand Down Expand Up @@ -591,6 +606,50 @@ impl Transaction {
}
}

impl DomainType for TransactionSummary {
type Proto = pbt::TransactionSummary;
}

impl From<TransactionSummary> for pbt::TransactionSummary {
fn from(summary: TransactionSummary) -> Self {
pbt::TransactionSummary {
effects: summary
.effects
.into_iter()
.map(|effect| pbt::transaction_summary::Effects {
address: Some(effect.address.into()),
balance: Some(effect.balance.into()),
})
.collect(),
}
}
}

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

fn try_from(pbt: pbt::TransactionSummary) -> Result<Self, Self::Error> {
let effects = pbt
.effects
.into_iter()
.map(|effect| {
Ok(TransactionEffect {
address: effect
.address
.ok_or_else(|| anyhow::anyhow!("missing address field"))?
.try_into()?,
balance: effect
.balance
.ok_or_else(|| anyhow::anyhow!("missing balance field"))?
.try_into()?,
})
})
.collect::<Result<Vec<TransactionEffect>, anyhow::Error>>()?;

Ok(Self { effects })
}
}

impl DomainType for TransactionBody {
type Proto = pbt::TransactionBody;
}
Expand Down Expand Up @@ -662,7 +721,12 @@ impl From<Transaction> for pbt::Transaction {

impl From<&Transaction> for pbt::Transaction {
fn from(msg: &Transaction) -> Self {
msg.into()
Transaction {
transaction_body: msg.transaction_body.clone(),
anchor: msg.anchor.clone(),
binding_sig: msg.binding_sig.clone(),
}
.into()
}
}

Expand Down
Loading

0 comments on commit c654809

Please sign in to comment.