From d299209142451bac43fe48db2f2f440693bd9c28 Mon Sep 17 00:00:00 2001 From: Lucas Meier Date: Wed, 29 Jan 2025 23:14:33 -0800 Subject: [PATCH] Implement stateless handler for LQT voting --- .../liquidity_tournament/mod.rs | 76 +++++++++++++++++++ .../funding/src/action_handler/mod.rs | 1 + crates/core/component/funding/src/lib.rs | 2 + 3 files changed, 79 insertions(+) create mode 100644 crates/core/component/funding/src/action_handler/liquidity_tournament/mod.rs create mode 100644 crates/core/component/funding/src/action_handler/mod.rs diff --git a/crates/core/component/funding/src/action_handler/liquidity_tournament/mod.rs b/crates/core/component/funding/src/action_handler/liquidity_tournament/mod.rs new file mode 100644 index 0000000000..0e5bb91054 --- /dev/null +++ b/crates/core/component/funding/src/action_handler/liquidity_tournament/mod.rs @@ -0,0 +1,76 @@ +use anyhow::Context as _; +use async_trait::async_trait; +use cnidarium::StateWrite; +use penumbra_sdk_asset::asset::Denom; +use penumbra_sdk_proof_params::DELEGATOR_VOTE_PROOF_VERIFICATION_KEY; +use penumbra_sdk_txhash::TransactionContext; + +use crate::liquidity_tournament::{ + proof::LiquidityTournamentVoteProofPublic, ActionLiquidityTournamentVote, + LiquidityTournamentVoteBody, LIQUIDITY_TOURNAMENT_VOTE_DENOM_MAX_BYTES, +}; +use cnidarium_component::ActionHandler; + +fn is_valid_denom(denom: &Denom) -> anyhow::Result<()> { + anyhow::ensure!( + denom.denom.len() <= LIQUIDITY_TOURNAMENT_VOTE_DENOM_MAX_BYTES, + "denom {} is not <= (MAX OF) {}", + &denom.denom, + LIQUIDITY_TOURNAMENT_VOTE_DENOM_MAX_BYTES + ); + anyhow::ensure!( + denom.denom.starts_with("transfer/"), + "denom {} is not an IBC transfer asset", + &denom.denom + ); + Ok(()) +} + +#[async_trait] +impl ActionHandler for ActionLiquidityTournamentVote { + type CheckStatelessContext = TransactionContext; + + async fn check_stateless(&self, context: TransactionContext) -> anyhow::Result<()> { + let Self { + auth_sig, + proof, + body: + LiquidityTournamentVoteBody { + start_position, + nullifier, + rk, + value, + incentivized, + .. + }, + } = self; + // 1. Is it ok to vote on this denom? + is_valid_denom(incentivized)?; + // 2. Check spend auth signature using provided spend auth key. + rk.verify(context.effect_hash.as_ref(), auth_sig) + .with_context(|| { + format!( + "{} auth signature failed to verify", + std::any::type_name::() + ) + })?; + + // 3. Verify the proof against the provided anchor and start position: + let public = LiquidityTournamentVoteProofPublic { + anchor: context.anchor, + value: *value, + nullifier: *nullifier, + rk: *rk, + start_position: *start_position, + }; + proof + .verify(&DELEGATOR_VOTE_PROOF_VERIFICATION_KEY, public) + .context("a LiquidityTournamentVote proof did not verify")?; + + Ok(()) + } + + async fn check_and_execute(&self, _state: S) -> anyhow::Result<()> { + todo!() + } +} diff --git a/crates/core/component/funding/src/action_handler/mod.rs b/crates/core/component/funding/src/action_handler/mod.rs new file mode 100644 index 0000000000..0a22bdcd17 --- /dev/null +++ b/crates/core/component/funding/src/action_handler/mod.rs @@ -0,0 +1 @@ +pub mod liquidity_tournament; diff --git a/crates/core/component/funding/src/lib.rs b/crates/core/component/funding/src/lib.rs index 83d154c5d5..6e11248cf4 100644 --- a/crates/core/component/funding/src/lib.rs +++ b/crates/core/component/funding/src/lib.rs @@ -1,6 +1,8 @@ #![deny(clippy::unwrap_used)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #[cfg(feature = "component")] +pub mod action_handler; +#[cfg(feature = "component")] pub mod component; pub mod event;