penumbra_sdk_sct/
source.rs1use anyhow::anyhow;
2use penumbra_sdk_proto::{core::component::sct::v1 as pb, DomainType};
3use penumbra_sdk_txhash::TransactionId;
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
7#[serde(try_from = "pb::CommitmentSource", into = "pb::CommitmentSource")]
8pub enum CommitmentSource {
9 Genesis,
11 Transaction {
13 id: Option<[u8; 32]>,
18 },
19 FundingStreamReward { epoch_index: u64 },
21 CommunityPoolOutput,
23 Ics20Transfer {
25 packet_seq: u64,
27 channel_id: String,
29 sender: String,
31 },
32 LiquidityTournamentReward {
34 epoch: u64,
36 tx_hash: TransactionId,
38 },
39}
40
41impl DomainType for CommitmentSource {
42 type Proto = pb::CommitmentSource;
43}
44
45impl CommitmentSource {
46 pub fn transaction() -> Self {
48 CommitmentSource::Transaction { id: None }
49 }
50
51 pub fn stripped(&self) -> Self {
53 match self {
54 CommitmentSource::Transaction { .. } => CommitmentSource::Transaction { id: None },
55 x => x.clone(),
56 }
57 }
58
59 pub fn id(&self) -> Option<[u8; 32]> {
61 match self {
62 CommitmentSource::Transaction { id: Some(id) } => Some(*id),
63 _ => None,
64 }
65 }
66}
67
68impl From<CommitmentSource> for pb::CommitmentSource {
69 fn from(value: CommitmentSource) -> Self {
70 use pb::commitment_source::{self as pbcs, Source};
71
72 Self {
73 source: Some(match value {
74 CommitmentSource::Genesis => Source::Genesis(pbcs::Genesis {}),
75 CommitmentSource::Transaction { id } => Source::Transaction(pbcs::Transaction {
76 id: id.map(|bytes| bytes.to_vec()).unwrap_or_default(),
77 }),
78 CommitmentSource::FundingStreamReward { epoch_index } => {
79 Source::FundingStreamReward(pbcs::FundingStreamReward { epoch_index })
80 }
81 CommitmentSource::CommunityPoolOutput => {
82 Source::CommunityPoolOutput(pbcs::CommunityPoolOutput {})
83 }
84 CommitmentSource::Ics20Transfer {
85 packet_seq,
86 channel_id,
87 sender,
88 } => Source::Ics20Transfer(pbcs::Ics20Transfer {
89 packet_seq,
90 channel_id,
91 sender,
92 }),
93 CommitmentSource::LiquidityTournamentReward { epoch, tx_hash } => {
94 Source::Lqt(pbcs::LiquidityTournamentReward {
95 epoch,
96 tx_hash: Some(tx_hash.into()),
97 })
98 }
99 }),
100 }
101 }
102}
103
104impl TryFrom<pb::CommitmentSource> for CommitmentSource {
105 type Error = anyhow::Error;
106
107 fn try_from(value: pb::CommitmentSource) -> Result<Self, Self::Error> {
108 use pb::commitment_source::Source;
109 let source = value.source.ok_or_else(|| anyhow!("missing source"))?;
110
111 Ok(match source {
112 Source::Genesis(_) => Self::Genesis,
113 Source::CommunityPoolOutput(_) => Self::CommunityPoolOutput,
114 Source::FundingStreamReward(x) => Self::FundingStreamReward {
115 epoch_index: x.epoch_index,
116 },
117 Source::Transaction(x) => {
118 if x.id.is_empty() {
119 Self::Transaction { id: None }
120 } else {
121 Self::Transaction {
122 id: Some(x.id.try_into().map_err(|id: Vec<u8>| {
123 anyhow!("expected 32-byte id array, got {} bytes", id.len())
124 })?),
125 }
126 }
127 }
128 Source::Ics20Transfer(x) => Self::Ics20Transfer {
129 packet_seq: x.packet_seq,
130 channel_id: x.channel_id,
131 sender: x.sender,
132 },
133 Source::Lqt(x) => Self::LiquidityTournamentReward {
134 epoch: x.epoch,
135 tx_hash: x
136 .tx_hash
137 .map(|x| x.try_into())
138 .transpose()?
139 .ok_or_else(|| anyhow!("missing LQT transaction hash"))?,
140 },
141 })
142 }
143}
144
145impl From<TransactionId> for CommitmentSource {
146 fn from(id: TransactionId) -> Self {
147 Self::Transaction { id: Some(id.0) }
148 }
149}