Skip to content

Commit

Permalink
feat: events for ibc transfers (backport)
Browse files Browse the repository at this point in the history
A backport of #4874 to v0.79.X, for reindexing purposes.
  • Loading branch information
cronokirby authored Oct 7, 2024
1 parent 2c3e46c commit 67989ab
Show file tree
Hide file tree
Showing 7 changed files with 978 additions and 4 deletions.
Binary file modified crates/cnidarium/src/gen/proto_descriptor.bin.no_lfs
Binary file not shown.
72 changes: 70 additions & 2 deletions crates/core/component/shielded-pool/src/component/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::str::FromStr;

use crate::{
component::{AssetRegistry, NoteManager},
Ics20Withdrawal,
event, Ics20Withdrawal,
};
use anyhow::{Context, Result};
use async_trait::async_trait;
Expand All @@ -23,6 +23,7 @@ use penumbra_asset::{asset, asset::Metadata, Value};
use penumbra_keys::Address;
use penumbra_num::Amount;
use penumbra_proto::{
core::component::shielded_pool::v1::FungibleTokenTransferPacketMetadata,
penumbra::core::component::ibc::v1::FungibleTokenPacketData, StateReadProto, StateWriteProto,
};
use penumbra_sct::CommitmentSource;
Expand All @@ -32,7 +33,7 @@ use penumbra_ibc::component::{
packet::{
IBCPacket, SendPacketRead as _, SendPacketWrite as _, Unchecked, WriteAcknowledgement as _,
},
state_key,
state_key, ChannelStateReadExt as _,
};

// returns a bool indicating if the provided denom was issued locally or if it was bridged in.
Expand Down Expand Up @@ -110,6 +111,20 @@ pub trait Ics20TransferWriteExt: StateWrite {
),
new_value_balance,
);
self.record_proto(event::outbound_fungible_token_transfer(
Value {
amount: withdrawal.amount,
asset_id: withdrawal.denom.id(),
},
&withdrawal.return_address,
withdrawal.destination_chain_address.clone(),
FungibleTokenTransferPacketMetadata {
channel: withdrawal.source_channel.0.clone(),
sequence: self
.get_send_sequence(&withdrawal.source_channel, checked_packet.source_port())
.await?,
},
));
} else {
// receiver is the source, burn utxos

Expand Down Expand Up @@ -143,6 +158,20 @@ pub trait Ics20TransferWriteExt: StateWrite {
),
new_value_balance,
);
self.record_proto(event::outbound_fungible_token_transfer(
Value {
amount: withdrawal.amount,
asset_id: withdrawal.denom.id(),
},
&withdrawal.return_address,
withdrawal.destination_chain_address.clone(),
FungibleTokenTransferPacketMetadata {
channel: withdrawal.source_channel.0.clone(),
sequence: self
.get_send_sequence(&withdrawal.source_channel, checked_packet.source_port())
.await?,
},
));
}

self.send_packet_execute(checked_packet).await;
Expand Down Expand Up @@ -349,6 +378,16 @@ async fn recv_transfer_packet_inner<S: StateWrite>(
state_key::ics20_value_balance::by_asset_id(&msg.packet.chan_on_b, &denom.id()),
new_value_balance,
);
state.record_proto(event::inbound_fungible_token_transfer(
value,
packet_data.sender.clone(),
&receiver_address,
// We want to use our name for the channel, hence b
FungibleTokenTransferPacketMetadata {
channel: msg.packet.chan_on_b.0.clone(),
sequence: msg.packet.sequence.0,
},
));
} else {
// create new denom:
//
Expand Down Expand Up @@ -400,6 +439,15 @@ async fn recv_transfer_packet_inner<S: StateWrite>(
state_key::ics20_value_balance::by_asset_id(&msg.packet.chan_on_b, &denom.id()),
new_value_balance,
);
state.record_proto(event::inbound_fungible_token_transfer(
value,
packet_data.sender.clone(),
&receiver_address,
FungibleTokenTransferPacketMetadata {
channel: msg.packet.chan_on_b.0.clone(),
sequence: msg.packet.sequence.0,
},
));
}

Ok(())
Expand Down Expand Up @@ -474,6 +522,16 @@ async fn timeout_packet_inner<S: StateWrite>(mut state: S, msg: &MsgTimeout) ->
state_key::ics20_value_balance::by_asset_id(&msg.packet.chan_on_a, &denom.id()),
new_value_balance,
);
state.record_proto(event::outbound_fungible_token_refund(
value,
&receiver, // note: this comes from packet_data.sender
packet_data.receiver.clone(),
event::FungibleTokenRefundReason::Timeout,
FungibleTokenTransferPacketMetadata {
channel: msg.packet.chan_on_a.0.clone(),
sequence: msg.packet.sequence.0,
},
));
} else {
let value_balance: Amount = state
.get(&state_key::ics20_value_balance::by_asset_id(
Expand Down Expand Up @@ -502,6 +560,16 @@ async fn timeout_packet_inner<S: StateWrite>(mut state: S, msg: &MsgTimeout) ->
state_key::ics20_value_balance::by_asset_id(&msg.packet.chan_on_a, &denom.id()),
new_value_balance,
);
state.record_proto(event::outbound_fungible_token_refund(
value,
&receiver, // note: this comes from packet_data.sender
packet_data.receiver.clone(),
event::FungibleTokenRefundReason::Timeout,
FungibleTokenTransferPacketMetadata {
channel: msg.packet.chan_on_a.0.clone(),
sequence: msg.packet.sequence.0,
},
));
}

Ok(())
Expand Down
63 changes: 61 additions & 2 deletions crates/core/component/shielded-pool/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use penumbra_asset::Value;
use penumbra_keys::Address;
use penumbra_proto::core::component::shielded_pool::v1::{
event_outbound_fungible_token_refund::Reason, EventInboundFungibleTokenTransfer,
EventOutboundFungibleTokenRefund, EventOutboundFungibleTokenTransfer, EventOutput, EventSpend,
FungibleTokenTransferPacketMetadata,
};
use penumbra_sct::Nullifier;

use penumbra_proto::core::component::shielded_pool::v1::{EventOutput, EventSpend};

use crate::NotePayload;

// These are sort of like the proto/domain type From impls, because
Expand All @@ -18,3 +23,57 @@ pub fn output(note_payload: &NotePayload) -> EventOutput {
note_commitment: Some(note_payload.note_commitment.into()),
}
}

pub fn outbound_fungible_token_transfer(
value: Value,
sender: &Address,
receiver: String,
meta: FungibleTokenTransferPacketMetadata,
) -> EventOutboundFungibleTokenTransfer {
EventOutboundFungibleTokenTransfer {
value: Some(value.into()),
sender: Some(sender.into()),
receiver,
meta: Some(meta),
}
}

#[derive(Clone, Copy, Debug)]
pub enum FungibleTokenRefundReason {
Timeout,
Error,
}

pub fn outbound_fungible_token_refund(
value: Value,
sender: &Address,
receiver: String,
reason: FungibleTokenRefundReason,
meta: FungibleTokenTransferPacketMetadata,
) -> EventOutboundFungibleTokenRefund {
let reason = match reason {
FungibleTokenRefundReason::Timeout => Reason::Timeout,
FungibleTokenRefundReason::Error => Reason::Error,
};
EventOutboundFungibleTokenRefund {
value: Some(value.into()),
sender: Some(sender.into()),
receiver,
reason: reason as i32,
meta: Some(meta),
}
}

pub fn inbound_fungible_token_transfer(
value: Value,
sender: String,
receiver: &Address,
meta: FungibleTokenTransferPacketMetadata,
) -> EventInboundFungibleTokenTransfer {
EventInboundFungibleTokenTransfer {
value: Some(value.into()),
sender,
receiver: Some(receiver.into()),
meta: Some(meta),
}
}
146 changes: 146 additions & 0 deletions crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,152 @@ impl ::prost::Name for AssetMetadataByIdsResponse {
)
}
}
/// Metadata about the packet associated with the transfer.
///
/// This allows identifying which specific packet is associated with the transfer.
/// Implicitly, both ports are going to be "transfer".
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct FungibleTokenTransferPacketMetadata {
/// The identifier for the channel on *this* chain.
#[prost(string, tag = "1")]
pub channel: ::prost::alloc::string::String,
/// Sequence number for the packet.
#[prost(uint64, tag = "2")]
pub sequence: u64,
}
impl ::prost::Name for FungibleTokenTransferPacketMetadata {
const NAME: &'static str = "FungibleTokenTransferPacketMetadata";
const PACKAGE: &'static str = "penumbra.core.component.shielded_pool.v1";
fn full_name() -> ::prost::alloc::string::String {
::prost::alloc::format!(
"penumbra.core.component.shielded_pool.v1.{}", Self::NAME
)
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EventOutboundFungibleTokenTransfer {
/// The value being transferred out of the chain.
#[prost(message, optional, tag = "1")]
pub value: ::core::option::Option<super::super::super::asset::v1::Value>,
/// The sending address on chain.
#[prost(message, optional, tag = "2")]
pub sender: ::core::option::Option<super::super::super::keys::v1::Address>,
/// The receiving address, which we don't assume anything about.
#[prost(string, tag = "3")]
pub receiver: ::prost::alloc::string::String,
#[prost(message, optional, tag = "4")]
pub meta: ::core::option::Option<FungibleTokenTransferPacketMetadata>,
}
impl ::prost::Name for EventOutboundFungibleTokenTransfer {
const NAME: &'static str = "EventOutboundFungibleTokenTransfer";
const PACKAGE: &'static str = "penumbra.core.component.shielded_pool.v1";
fn full_name() -> ::prost::alloc::string::String {
::prost::alloc::format!(
"penumbra.core.component.shielded_pool.v1.{}", Self::NAME
)
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EventOutboundFungibleTokenRefund {
/// The value being refunded.
#[prost(message, optional, tag = "1")]
pub value: ::core::option::Option<super::super::super::asset::v1::Value>,
/// The sender being refunded.
#[prost(message, optional, tag = "2")]
pub sender: ::core::option::Option<super::super::super::keys::v1::Address>,
/// The address that attempted to receive the funds.
#[prost(string, tag = "3")]
pub receiver: ::prost::alloc::string::String,
/// Why the refund is happening.
#[prost(enumeration = "event_outbound_fungible_token_refund::Reason", tag = "4")]
pub reason: i32,
/// This will be the metadata for the packet for the transfer being refunded.
///
/// This allows linking a refund to the transfer.
#[prost(message, optional, tag = "5")]
pub meta: ::core::option::Option<FungibleTokenTransferPacketMetadata>,
}
/// Nested message and enum types in `EventOutboundFungibleTokenRefund`.
pub mod event_outbound_fungible_token_refund {
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
::prost::Enumeration
)]
#[repr(i32)]
pub enum Reason {
/// No particular reason.
Unspecified = 0,
/// The transfer timed out.
Timeout = 1,
/// The transfer was acknowledged with an error.
Error = 2,
}
impl Reason {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Reason::Unspecified => "REASON_UNSPECIFIED",
Reason::Timeout => "REASON_TIMEOUT",
Reason::Error => "REASON_ERROR",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"REASON_UNSPECIFIED" => Some(Self::Unspecified),
"REASON_TIMEOUT" => Some(Self::Timeout),
"REASON_ERROR" => Some(Self::Error),
_ => None,
}
}
}
}
impl ::prost::Name for EventOutboundFungibleTokenRefund {
const NAME: &'static str = "EventOutboundFungibleTokenRefund";
const PACKAGE: &'static str = "penumbra.core.component.shielded_pool.v1";
fn full_name() -> ::prost::alloc::string::String {
::prost::alloc::format!(
"penumbra.core.component.shielded_pool.v1.{}", Self::NAME
)
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EventInboundFungibleTokenTransfer {
/// The value being transferred in.
#[prost(message, optional, tag = "1")]
pub value: ::core::option::Option<super::super::super::asset::v1::Value>,
/// The sender on the counterparty chain.
#[prost(string, tag = "2")]
pub sender: ::prost::alloc::string::String,
/// The receiver on this chain.
#[prost(message, optional, tag = "3")]
pub receiver: ::core::option::Option<super::super::super::keys::v1::Address>,
#[prost(message, optional, tag = "4")]
pub meta: ::core::option::Option<FungibleTokenTransferPacketMetadata>,
}
impl ::prost::Name for EventInboundFungibleTokenTransfer {
const NAME: &'static str = "EventInboundFungibleTokenTransfer";
const PACKAGE: &'static str = "penumbra.core.component.shielded_pool.v1";
fn full_name() -> ::prost::alloc::string::String {
::prost::alloc::format!(
"penumbra.core.component.shielded_pool.v1.{}", Self::NAME
)
}
}
/// Generated client implementations.
#[cfg(feature = "rpc")]
pub mod query_service_client {
Expand Down
Loading

0 comments on commit 67989ab

Please sign in to comment.