penumbra_sdk_app/action_handler/transaction/
stateless.rs1use anyhow::{Context, Result};
2use penumbra_sdk_transaction::Transaction;
3use penumbra_sdk_txhash::AuthorizingData;
4
5#[tracing::instrument(skip(tx))]
6pub(super) fn valid_binding_signature(tx: &Transaction) -> Result<()> {
7 let auth_hash = tx.auth_hash();
8
9 tracing::debug!(bvk = ?tx.binding_verification_key(), ?auth_hash);
10
11 tx.binding_verification_key()
13 .verify(auth_hash.as_bytes(), tx.binding_sig())
14 .context("binding signature failed to verify")
15}
16
17pub fn num_clues_equal_to_num_outputs(tx: &Transaction) -> anyhow::Result<()> {
18 if tx
19 .transaction_body()
20 .detection_data
21 .unwrap_or_default()
22 .fmd_clues
23 .len()
24 != tx.outputs().count()
25 {
26 Err(anyhow::anyhow!(
27 "consensus rule violated: must have equal number of outputs and FMD clues"
28 ))
29 } else {
30 Ok(())
31 }
32}
33
34#[allow(clippy::if_same_then_else)]
35pub fn check_memo_exists_if_outputs_absent_if_not(tx: &Transaction) -> anyhow::Result<()> {
36 let num_outputs = tx.outputs().count();
37 if num_outputs > 0 && tx.transaction_body().memo.is_none() {
38 Err(anyhow::anyhow!(
39 "consensus rule violated: must have memo if outputs present"
40 ))
41 } else if num_outputs > 0 && tx.transaction_body().memo.is_some() {
42 Ok(())
43 } else if num_outputs == 0 && tx.transaction_body().memo.is_none() {
44 Ok(())
45 } else {
46 Err(anyhow::anyhow!(
47 "consensus rule violated: cannot have memo if no outputs present"
48 ))
49 }
50}
51
52pub fn check_non_empty_transaction(tx: &Transaction) -> anyhow::Result<()> {
53 let num_actions = tx.actions().count();
54 if num_actions > 0 {
55 Ok(())
56 } else {
57 Err(anyhow::anyhow!(
58 "consensus rule violated: transaction must have more than 0 actions"
59 ))
60 }
61}