chia_sdk_driver/layers/action_layer/
m_of_n_layer.rs

1use chia_bls::PublicKey;
2use chia_protocol::{Bytes32, Coin};
3use chia_sdk_types::{
4    puzzles::{
5        P2MOfNDelegateDirectArgs, P2MOfNDelegateDirectSolution,
6        P2_M_OF_N_DELEGATE_DIRECT_PUZZLE_HASH,
7    },
8    Condition, Conditions,
9};
10use clvm_traits::{clvm_quote, FromClvm};
11use clvm_utils::{ToTreeHash, TreeHash};
12use clvmr::{Allocator, NodePtr};
13
14use crate::{DriverError, Layer, Puzzle, Spend, SpendContext};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct MOfNLayer {
18    pub m: usize,
19    pub public_key_list: Vec<PublicKey>,
20}
21
22impl Layer for MOfNLayer {
23    type Solution = P2MOfNDelegateDirectSolution<NodePtr, NodePtr>;
24
25    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
26        ctx.curry(P2MOfNDelegateDirectArgs::new(
27            self.m,
28            self.public_key_list.clone(),
29        ))
30    }
31
32    fn construct_solution(
33        &self,
34        ctx: &mut SpendContext,
35        solution: Self::Solution,
36    ) -> Result<NodePtr, DriverError> {
37        ctx.alloc(&solution)
38    }
39
40    fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
41        let Some(puzzle) = puzzle.as_curried() else {
42            return Ok(None);
43        };
44
45        if puzzle.mod_hash != P2_M_OF_N_DELEGATE_DIRECT_PUZZLE_HASH {
46            return Ok(None);
47        }
48
49        let args = P2MOfNDelegateDirectArgs::from_clvm(allocator, puzzle.args)?;
50
51        Ok(Some(Self {
52            m: args.m,
53            public_key_list: args.public_key_list,
54        }))
55    }
56
57    fn parse_solution(
58        allocator: &Allocator,
59        solution: NodePtr,
60    ) -> Result<Self::Solution, DriverError> {
61        Ok(P2MOfNDelegateDirectSolution::from_clvm(
62            allocator, solution,
63        )?)
64    }
65}
66
67impl MOfNLayer {
68    pub fn new(m: usize, public_key_list: Vec<PublicKey>) -> Self {
69        Self { m, public_key_list }
70    }
71
72    pub fn ensure_non_replayable(
73        conditions: Conditions,
74        coin_id: Bytes32,
75        genesis_challenge: NodePtr,
76    ) -> Conditions {
77        let found_condition = conditions.clone().into_iter().find(|c| {
78            matches!(c, Condition::AssertMyCoinId(..))
79                || matches!(c, Condition::AssertMyParentId(..))
80        });
81
82        if found_condition.is_some() {
83            conditions
84        } else {
85            conditions.assert_my_coin_id(coin_id)
86        }
87        .remark(genesis_challenge)
88    }
89
90    pub fn spend(
91        &self,
92        ctx: &mut SpendContext,
93        coin: Coin,
94        conditions: Conditions,
95        used_pubkeys: &[PublicKey],
96        genesis_challenge: Bytes32,
97    ) -> Result<(), DriverError> {
98        let genesis_challenge = ctx.alloc(&genesis_challenge)?;
99        let spend = self.spend_with_conditions(
100            ctx,
101            Self::ensure_non_replayable(conditions, coin.coin_id(), genesis_challenge),
102            used_pubkeys,
103        )?;
104        ctx.spend(coin, spend)
105    }
106
107    pub fn spend_with_conditions(
108        &self,
109        ctx: &mut SpendContext,
110        conditions: Conditions,
111        used_pubkeys: &[PublicKey],
112    ) -> Result<Spend, DriverError> {
113        let delegated_puzzle = ctx.alloc(&clvm_quote!(conditions))?;
114        self.construct_spend(
115            ctx,
116            P2MOfNDelegateDirectSolution {
117                selectors: P2MOfNDelegateDirectArgs::selectors_for_used_pubkeys(
118                    &self.public_key_list,
119                    used_pubkeys,
120                ),
121                delegated_puzzle,
122                delegated_solution: NodePtr::NIL,
123            },
124        )
125    }
126}
127
128impl ToTreeHash for MOfNLayer {
129    fn tree_hash(&self) -> TreeHash {
130        P2MOfNDelegateDirectArgs::curry_tree_hash(self.m, self.public_key_list.clone())
131    }
132}