penumbra_sdk_shielded_pool/component/action_handler/
spend.rs

1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use cnidarium::StateWrite;
4use cnidarium_component::ActionHandler;
5use penumbra_sdk_proof_params::SPEND_PROOF_VERIFICATION_KEY;
6use penumbra_sdk_proto::{DomainType, StateWriteProto as _};
7use penumbra_sdk_sct::component::{
8    source::SourceContext,
9    tree::{SctManager, VerificationExt},
10};
11use penumbra_sdk_txhash::TransactionContext;
12
13use crate::{event, Spend, SpendProofPublic};
14
15#[async_trait]
16impl ActionHandler for Spend {
17    type CheckStatelessContext = TransactionContext;
18    async fn check_stateless(&self, context: TransactionContext) -> Result<()> {
19        let spend = self;
20        // 2. Check spend auth signature using provided spend auth key.
21        spend
22            .body
23            .rk
24            .verify(context.effect_hash.as_ref(), &spend.auth_sig)
25            .context("spend auth signature failed to verify")?;
26
27        // 3. Check that the proof verifies.
28        let public = SpendProofPublic {
29            anchor: context.anchor,
30            balance_commitment: spend.body.balance_commitment,
31            nullifier: spend.body.nullifier,
32            rk: spend.body.rk,
33        };
34        spend
35            .proof
36            .verify(&SPEND_PROOF_VERIFICATION_KEY, public)
37            .context("a spend proof did not verify")?;
38
39        Ok(())
40    }
41
42    async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> Result<()> {
43        // Check that the `Nullifier` has not been spent before.
44        let spent_nullifier = self.body.nullifier;
45        state.check_nullifier_unspent(spent_nullifier).await?;
46
47        let source = state.get_current_source().expect("source should be set");
48
49        state.nullify(self.body.nullifier, source).await;
50
51        // Also record an ABCI event for transaction indexing.
52        state.record_proto(
53            event::EventSpend {
54                nullifier: self.body.nullifier,
55            }
56            .to_proto(),
57        );
58
59        Ok(())
60    }
61}