Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sct: extend commitment source with LQT variant #5024

Merged
merged 4 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions crates/bin/pcli/src/command/view/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ fn format_source(source: &CommitmentSource) -> String {
"ICS20 packet {} via {} from {}",
packet_seq, channel_id, sender
),
CommitmentSource::LiquidityTournamentReward { epoch, tx_hash } => {
format!(
"Liquidity tournament reward (Epoch {}, Tx {})",
epoch, tx_hash
)
}
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/core/component/sct/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pbjson-types = {workspace = true}
penumbra-sdk-keys = {workspace = true, default-features = false}
penumbra-sdk-proto = {workspace = true, default-features = false}
penumbra-sdk-tct = {workspace = true, default-features = true}
penumbra-sdk-txhash = {workspace = true, default-features = false}
poseidon377 = {workspace = true, features = ["r1cs"]}
rand = {workspace = true}
rand_core = {workspace = true, features = ["getrandom"]}
Expand Down
22 changes: 22 additions & 0 deletions crates/core/component/sct/src/source.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::anyhow;
use penumbra_sdk_proto::{core::component::sct::v1 as pb, DomainType};
use penumbra_sdk_txhash::TransactionId;
use serde::{Deserialize, Serialize};

#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
Expand Down Expand Up @@ -28,6 +29,13 @@ pub enum CommitmentSource {
/// The sender address on the counterparty chain.
sender: String,
},
/// The commitment was created through the participation in the liquidity tournament.
LiquidityTournamentReward {
/// The epoch in which the reward occured.
epoch: u64,
/// Transaction hash of the transaction that did the voting.
tx_hash: TransactionId,
},
}

impl DomainType for CommitmentSource {
Expand Down Expand Up @@ -82,6 +90,12 @@ impl From<CommitmentSource> for pb::CommitmentSource {
channel_id,
sender,
}),
CommitmentSource::LiquidityTournamentReward { epoch, tx_hash } => {
Source::Lqt(pbcs::LiquidityTournamentReward {
epoch,
tx_hash: Some(tx_hash.into()),
})
}
}),
}
}
Expand Down Expand Up @@ -116,6 +130,14 @@ impl TryFrom<pb::CommitmentSource> for CommitmentSource {
channel_id: x.channel_id,
sender: x.sender,
},
Source::Lqt(x) => Self::LiquidityTournamentReward {
epoch: x.epoch,
tx_hash: x
.tx_hash
.map(|x| x.try_into())
.transpose()?
.ok_or_else(|| anyhow!("missing LQT transaction hash"))?,
},
})
}
}
28 changes: 27 additions & 1 deletion crates/proto/src/gen/penumbra.core.component.sct.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl ::prost::Name for Epoch {
/// decide whether or not to download block data.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct CommitmentSource {
#[prost(oneof = "commitment_source::Source", tags = "1, 2, 20, 30, 40")]
#[prost(oneof = "commitment_source::Source", tags = "1, 2, 20, 30, 40, 50")]
pub source: ::core::option::Option<commitment_source::Source>,
}
/// Nested message and enum types in `CommitmentSource`.
Expand Down Expand Up @@ -154,6 +154,30 @@ pub mod commitment_source {
"/penumbra.core.component.sct.v1.CommitmentSource.Ics20Transfer".into()
}
}
/// The commitment was created by the LQT mechanism and tracks LQT reward notes.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct LiquidityTournamentReward {
/// The epoch in which the reward occured.
#[prost(uint64, tag = "1")]
pub epoch: u64,
/// Transaction hash of the transaction that did the voting.
#[prost(message, optional, tag = "2")]
pub tx_hash: ::core::option::Option<
super::super::super::super::txhash::v1::TransactionId,
>,
}
impl ::prost::Name for LiquidityTournamentReward {
const NAME: &'static str = "LiquidityTournamentReward";
const PACKAGE: &'static str = "penumbra.core.component.sct.v1";
fn full_name() -> ::prost::alloc::string::String {
"penumbra.core.component.sct.v1.CommitmentSource.LiquidityTournamentReward"
.into()
}
fn type_url() -> ::prost::alloc::string::String {
"/penumbra.core.component.sct.v1.CommitmentSource.LiquidityTournamentReward"
.into()
}
}
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Source {
#[prost(message, tag = "1")]
Expand All @@ -166,6 +190,8 @@ pub mod commitment_source {
CommunityPoolOutput(CommunityPoolOutput),
#[prost(message, tag = "40")]
Genesis(Genesis),
#[prost(message, tag = "50")]
Lqt(LiquidityTournamentReward),
}
}
impl ::prost::Name for CommitmentSource {
Expand Down
130 changes: 130 additions & 0 deletions crates/proto/src/gen/penumbra.core.component.sct.v1.serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ impl serde::Serialize for CommitmentSource {
commitment_source::Source::Genesis(v) => {
struct_ser.serialize_field("genesis", v)?;
}
commitment_source::Source::Lqt(v) => {
struct_ser.serialize_field("lqt", v)?;
}
}
}
struct_ser.end()
Expand All @@ -241,6 +244,7 @@ impl<'de> serde::Deserialize<'de> for CommitmentSource {
"community_pool_output",
"communityPoolOutput",
"genesis",
"lqt",
];

#[allow(clippy::enum_variant_names)]
Expand All @@ -250,6 +254,7 @@ impl<'de> serde::Deserialize<'de> for CommitmentSource {
FundingStreamReward,
CommunityPoolOutput,
Genesis,
Lqt,
__SkipField__,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
Expand Down Expand Up @@ -277,6 +282,7 @@ impl<'de> serde::Deserialize<'de> for CommitmentSource {
"fundingStreamReward" | "funding_stream_reward" => Ok(GeneratedField::FundingStreamReward),
"communityPoolOutput" | "community_pool_output" => Ok(GeneratedField::CommunityPoolOutput),
"genesis" => Ok(GeneratedField::Genesis),
"lqt" => Ok(GeneratedField::Lqt),
_ => Ok(GeneratedField::__SkipField__),
}
}
Expand Down Expand Up @@ -332,6 +338,13 @@ impl<'de> serde::Deserialize<'de> for CommitmentSource {
return Err(serde::de::Error::duplicate_field("genesis"));
}
source__ = map_.next_value::<::std::option::Option<_>>()?.map(commitment_source::Source::Genesis)
;
}
GeneratedField::Lqt => {
if source__.is_some() {
return Err(serde::de::Error::duplicate_field("lqt"));
}
source__ = map_.next_value::<::std::option::Option<_>>()?.map(commitment_source::Source::Lqt)
;
}
GeneratedField::__SkipField__ => {
Expand Down Expand Up @@ -726,6 +739,123 @@ impl<'de> serde::Deserialize<'de> for commitment_source::Ics20Transfer {
deserializer.deserialize_struct("penumbra.core.component.sct.v1.CommitmentSource.Ics20Transfer", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for commitment_source::LiquidityTournamentReward {
#[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.epoch != 0 {
len += 1;
}
if self.tx_hash.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("penumbra.core.component.sct.v1.CommitmentSource.LiquidityTournamentReward", len)?;
if self.epoch != 0 {
#[allow(clippy::needless_borrow)]
#[allow(clippy::needless_borrows_for_generic_args)]
struct_ser.serialize_field("epoch", ToString::to_string(&self.epoch).as_str())?;
}
if let Some(v) = self.tx_hash.as_ref() {
struct_ser.serialize_field("txHash", v)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for commitment_source::LiquidityTournamentReward {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &[
"epoch",
"tx_hash",
"txHash",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Epoch,
TxHash,
__SkipField__,
}
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 {
"epoch" => Ok(GeneratedField::Epoch),
"txHash" | "tx_hash" => Ok(GeneratedField::TxHash),
_ => Ok(GeneratedField::__SkipField__),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = commitment_source::LiquidityTournamentReward;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct penumbra.core.component.sct.v1.CommitmentSource.LiquidityTournamentReward")
}

fn visit_map<V>(self, mut map_: V) -> std::result::Result<commitment_source::LiquidityTournamentReward, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut epoch__ = None;
let mut tx_hash__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Epoch => {
if epoch__.is_some() {
return Err(serde::de::Error::duplicate_field("epoch"));
}
epoch__ =
Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0)
;
}
GeneratedField::TxHash => {
if tx_hash__.is_some() {
return Err(serde::de::Error::duplicate_field("txHash"));
}
tx_hash__ = map_.next_value()?;
}
GeneratedField::__SkipField__ => {
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
}
}
}
Ok(commitment_source::LiquidityTournamentReward {
epoch: epoch__.unwrap_or_default(),
tx_hash: tx_hash__,
})
}
}
deserializer.deserialize_struct("penumbra.core.component.sct.v1.CommitmentSource.LiquidityTournamentReward", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for commitment_source::Transaction {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
Expand Down
9 changes: 9 additions & 0 deletions proto/penumbra/penumbra/core/component/sct/v1/sct.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto3";
package penumbra.core.component.sct.v1;

import "penumbra/crypto/tct/v1/tct.proto";
import "penumbra/core/txhash/v1/txhash.proto";
import "google/protobuf/timestamp.proto";

// Configuration data for the SCT component.
Expand Down Expand Up @@ -59,12 +60,20 @@ message CommitmentSource {
// The sender address on the counterparty chain
string sender = 3;
}
// The commitment was created by the LQT mechanism and tracks LQT reward notes.
message LiquidityTournamentReward {
// The epoch in which the reward occured.
uint64 epoch = 1;
// Transaction hash of the transaction that did the voting.
txhash.v1.TransactionId tx_hash = 2;
}
oneof source {
Transaction transaction = 1;
Ics20Transfer ics_20_transfer = 2;
FundingStreamReward funding_stream_reward = 20;
CommunityPoolOutput community_pool_output = 30;
Genesis genesis = 40;
LiquidityTournamentReward lqt = 50;
}
}

Expand Down
Loading