chia_sdk_driver/action_system/
spend_kind.rs

1use chia_protocol::Bytes32;
2use chia_puzzle_types::offer::{NotarizedPayment, Payment};
3use chia_sdk_types::{
4    Conditions,
5    conditions::{AssertPuzzleAnnouncement, CreateCoin},
6    payment_assertion, tree_hash_notarized_payment,
7};
8use clvmr::{Allocator, NodePtr};
9use rand::{Rng, SeedableRng};
10use rand_chacha::ChaCha20Rng;
11
12use crate::{Output, OutputConstraints, OutputSet};
13
14mod conditions_spend;
15mod settlement_spend;
16
17pub use conditions_spend::*;
18pub use settlement_spend::*;
19
20#[derive(Debug, Clone)]
21pub enum SpendKind {
22    Conditions(ConditionsSpend),
23    Settlement(SettlementSpend),
24}
25
26impl SpendKind {
27    pub fn conditions() -> Self {
28        Self::Conditions(ConditionsSpend::new())
29    }
30
31    pub fn settlement() -> Self {
32        Self::Settlement(SettlementSpend::new())
33    }
34
35    pub fn is_conditions(&self) -> bool {
36        matches!(self, Self::Conditions(_))
37    }
38
39    pub fn is_settlement(&self) -> bool {
40        matches!(self, Self::Settlement(_))
41    }
42
43    pub fn create_coin_with_assertion(
44        &mut self,
45        allocator: &Allocator,
46        parent_puzzle_hash: Bytes32,
47        payment_assertions: &mut Vec<AssertPuzzleAnnouncement>,
48        create_coin: CreateCoin<NodePtr>,
49    ) {
50        match self {
51            SpendKind::Conditions(spend) => {
52                spend.add_conditions(Conditions::new().with(create_coin));
53            }
54            SpendKind::Settlement(spend) => {
55                // TODO: Use nil for the nonce from the payment
56                let notarized_payment = NotarizedPayment::new(
57                    Bytes32::default(),
58                    vec![Payment::new(
59                        create_coin.puzzle_hash,
60                        create_coin.amount,
61                        create_coin.memos,
62                    )],
63                );
64                payment_assertions.push(payment_assertion(
65                    parent_puzzle_hash,
66                    tree_hash_notarized_payment(allocator, &notarized_payment),
67                ));
68                spend.add_notarized_payment(notarized_payment);
69            }
70        }
71    }
72
73    pub fn create_intermediate_coin(&mut self, create_coin: CreateCoin<NodePtr>) {
74        match self {
75            Self::Conditions(spend) => {
76                spend.add_conditions(Conditions::new().with(create_coin));
77            }
78            Self::Settlement(spend) => {
79                spend.add_notarized_payment(NotarizedPayment {
80                    nonce: Bytes32::new(ChaCha20Rng::from_os_rng().random()),
81                    payments: vec![Payment::new(
82                        create_coin.puzzle_hash,
83                        create_coin.amount,
84                        create_coin.memos,
85                    )],
86                });
87            }
88        }
89    }
90
91    #[must_use]
92    pub fn empty_copy(&self) -> Self {
93        match self {
94            Self::Conditions(_) => Self::conditions(),
95            Self::Settlement(_) => Self::settlement(),
96        }
97    }
98}
99
100impl OutputSet for SpendKind {
101    fn has_output(&self, output: &Output) -> bool {
102        match self {
103            Self::Conditions(spend) => spend.has_output(output),
104            Self::Settlement(spend) => spend.has_output(output),
105        }
106    }
107
108    fn can_run_cat_tail(&self) -> bool {
109        match self {
110            Self::Conditions(spend) => spend.can_run_cat_tail(),
111            Self::Settlement(spend) => spend.can_run_cat_tail(),
112        }
113    }
114
115    fn missing_singleton_output(&self) -> bool {
116        match self {
117            Self::Conditions(spend) => spend.missing_singleton_output(),
118            Self::Settlement(spend) => spend.missing_singleton_output(),
119        }
120    }
121
122    fn is_allowed(&self, output: &Output, output_constraints: &OutputConstraints) -> bool {
123        match self {
124            Self::Conditions(spend) => spend.is_allowed(output, output_constraints),
125            Self::Settlement(spend) => spend.is_allowed(output, output_constraints),
126        }
127    }
128}