penumbra_sdk_dex/component/action_handler/
swap_claim.rs1use std::sync::Arc;
2
3use anyhow::{Context, Result};
4use async_trait::async_trait;
5use cnidarium_component::ActionHandler;
6use penumbra_sdk_txhash::TransactionContext;
7
8use cnidarium::{StateRead, StateWrite};
9use penumbra_sdk_proof_params::SWAPCLAIM_PROOF_VERIFICATION_KEY;
10use penumbra_sdk_proto::{DomainType as _, StateWriteProto};
11use penumbra_sdk_sct::component::{
12 source::SourceContext,
13 tree::{SctManager, VerificationExt},
14 StateReadExt as _,
15};
16use penumbra_sdk_shielded_pool::component::NoteManager;
17
18use crate::{
19 component::StateReadExt,
20 event,
21 swap_claim::{SwapClaim, SwapClaimProofPublic},
22};
23
24#[async_trait]
25impl ActionHandler for SwapClaim {
26 type CheckStatelessContext = TransactionContext;
27 async fn check_stateless(&self, context: TransactionContext) -> Result<()> {
28 self.proof
29 .verify(
30 &SWAPCLAIM_PROOF_VERIFICATION_KEY,
31 SwapClaimProofPublic {
32 anchor: context.anchor,
33 nullifier: self.body.nullifier,
34 claim_fee: self.body.fee.clone(),
35 output_data: self.body.output_data,
36 note_commitment_1: self.body.output_1_commitment,
37 note_commitment_2: self.body.output_2_commitment,
38 },
39 )
40 .context("a swap claim proof did not verify")?;
41
42 Ok(())
43 }
44
45 async fn check_historical<S: StateRead + 'static>(&self, state: Arc<S>) -> Result<()> {
46 let swap_claim = self;
47
48 let epoch_duration = state.get_epoch_duration_parameter().await?;
53 let provided_epoch_duration = swap_claim.epoch_duration;
54 if epoch_duration != provided_epoch_duration {
55 anyhow::bail!("provided epoch duration does not match chain epoch duration");
56 }
57
58 let provided_output_height = swap_claim.body.output_data.height;
64 let provided_trading_pair = swap_claim.body.output_data.trading_pair;
65 let output_data = state
66 .output_data(provided_output_height, provided_trading_pair)
67 .await?
68 .ok_or_else(|| anyhow::anyhow!("output data not found"))?;
71
72 if output_data != swap_claim.body.output_data {
73 anyhow::bail!("provided output data does not match chain output data");
74 }
75
76 Ok(())
77 }
78
79 async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> Result<()> {
80 let spent_nullifier = self.body.nullifier;
82 state.check_nullifier_unspent(spent_nullifier).await?;
83
84 let source = state
86 .get_current_source()
87 .expect("source is set during tx execution");
88
89 state
90 .add_rolled_up_payload(self.body.output_1_commitment, source.clone())
91 .await;
92 state
93 .add_rolled_up_payload(self.body.output_2_commitment, source.clone())
94 .await;
95
96 state.nullify(self.body.nullifier, source).await;
97
98 state.record_proto(event::EventSwapClaim::from(self).to_proto());
99
100 Ok(())
101 }
102}