chia_sdk_driver/layers/
p2_delegated_singleton_layer.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
use chia_protocol::{Bytes32, Coin};
use chia_puzzles::singleton::{SINGLETON_LAUNCHER_PUZZLE_HASH, SINGLETON_TOP_LAYER_PUZZLE_HASH};
use clvm_traits::{FromClvm, ToClvm};
use clvm_utils::{CurriedProgram, ToTreeHash, TreeHash};
use clvmr::{Allocator, NodePtr};
use hex_literal::hex;

use crate::{DriverError, Layer, Puzzle, Spend, SpendContext};

/// The p2 delegated singleton [`Layer`] allows for requiring that a singleton
/// be spent alongside this coin to authorize it, while also outputting conditions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct P2DelegatedSingletonLayer {
    pub launcher_id: Bytes32,
}

impl P2DelegatedSingletonLayer {
    pub fn new(launcher_id: Bytes32) -> Self {
        Self { launcher_id }
    }

    pub fn spend(
        &self,
        ctx: &mut SpendContext,
        coin_id: Bytes32,
        singleton_inner_puzzle_hash: Bytes32,
        delegated_spend: Spend,
    ) -> Result<Spend, DriverError> {
        let puzzle = self.construct_puzzle(ctx)?;
        let solution = self.construct_solution(
            ctx,
            P2DelegatedSingletonSolution {
                singleton_inner_puzzle_hash,
                coin_id,
                delegated_puzzle: delegated_spend.puzzle,
                delegated_solution: delegated_spend.solution,
            },
        )?;
        Ok(Spend { puzzle, solution })
    }

    pub fn spend_coin(
        &self,
        ctx: &mut SpendContext,
        coin: Coin,
        singleton_inner_puzzle_hash: Bytes32,
        delegated_spend: Spend,
    ) -> Result<(), DriverError> {
        let coin_spend = self.construct_coin_spend(
            ctx,
            coin,
            P2DelegatedSingletonSolution {
                singleton_inner_puzzle_hash,
                coin_id: coin.coin_id(),
                delegated_puzzle: delegated_spend.puzzle,
                delegated_solution: delegated_spend.solution,
            },
        )?;
        ctx.insert(coin_spend);
        Ok(())
    }
}

impl Layer for P2DelegatedSingletonLayer {
    type Solution = P2DelegatedSingletonSolution<NodePtr, NodePtr>;

    fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
        let Some(puzzle) = puzzle.as_curried() else {
            return Ok(None);
        };

        if puzzle.mod_hash != P2_DELEGATED_SINGLETON_PUZZLE_HASH {
            return Ok(None);
        }

        let args = P2DelegatedSingletonArgs::from_clvm(allocator, puzzle.args)?;

        if args.singleton_mod_hash != SINGLETON_TOP_LAYER_PUZZLE_HASH.into()
            || args.launcher_puzzle_hash != SINGLETON_LAUNCHER_PUZZLE_HASH.into()
        {
            return Err(DriverError::InvalidSingletonStruct);
        }

        Ok(Some(Self {
            launcher_id: args.launcher_id,
        }))
    }

    fn parse_solution(
        allocator: &Allocator,
        solution: NodePtr,
    ) -> Result<Self::Solution, DriverError> {
        Ok(P2DelegatedSingletonSolution::from_clvm(
            allocator, solution,
        )?)
    }

    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
        let curried = CurriedProgram {
            program: ctx.p2_delegated_singleton_puzzle()?,
            args: P2DelegatedSingletonArgs::new(self.launcher_id),
        };
        ctx.alloc(&curried)
    }

    fn construct_solution(
        &self,
        ctx: &mut SpendContext,
        solution: Self::Solution,
    ) -> Result<NodePtr, DriverError> {
        ctx.alloc(&solution)
    }
}

impl ToTreeHash for P2DelegatedSingletonLayer {
    fn tree_hash(&self) -> TreeHash {
        P2DelegatedSingletonArgs::curry_tree_hash(self.launcher_id)
    }
}

// (mod (SINGLETON_MOD_HASH LAUNCHER_ID LAUNCHER_PUZZLE_HASH singleton_inner_puzzle_hash delegated_puzzle delegated_solution my_id)

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[clvm(curry)]
pub struct P2DelegatedSingletonArgs {
    pub singleton_mod_hash: Bytes32,
    pub launcher_id: Bytes32,
    pub launcher_puzzle_hash: Bytes32,
}

impl P2DelegatedSingletonArgs {
    pub fn new(launcher_id: Bytes32) -> Self {
        Self {
            singleton_mod_hash: SINGLETON_TOP_LAYER_PUZZLE_HASH.into(),
            launcher_id,
            launcher_puzzle_hash: SINGLETON_LAUNCHER_PUZZLE_HASH.into(),
        }
    }

    pub fn curry_tree_hash(launcher_id: Bytes32) -> TreeHash {
        CurriedProgram {
            program: P2_DELEGATED_SINGLETON_PUZZLE_HASH,
            args: Self::new(launcher_id),
        }
        .tree_hash()
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[clvm(list)]
pub struct P2DelegatedSingletonSolution<P, S> {
    pub singleton_inner_puzzle_hash: Bytes32,
    pub delegated_puzzle: P,
    pub delegated_solution: S,
    pub coin_id: Bytes32,
}

pub const P2_DELEGATED_SINGLETON_PUZZLE: [u8; 508] = hex!(
    "
    ff02ffff01ff02ff16ffff04ff02ffff04ffff04ffff04ff28ffff04ffff0bff
    ff02ff2effff04ff02ffff04ff05ffff04ff2fffff04ffff02ff3effff04ff02
    ffff04ffff04ff05ffff04ff0bff178080ff80808080ff808080808080ff8201
    7f80ff808080ffff04ffff04ff14ffff04ffff02ff3effff04ff02ffff04ff5f
    ff80808080ff808080ffff04ffff04ff10ffff04ff82017fff808080ff808080
    80ffff04ffff02ff5fff81bf80ff8080808080ffff04ffff01ffffff46ff3f02
    ff3cff0401ffff01ff02ff02ffff03ff05ffff01ff02ff3affff04ff02ffff04
    ff0dffff04ffff0bff2affff0bff3cff2c80ffff0bff2affff0bff2affff0bff
    3cff1280ff0980ffff0bff2aff0bffff0bff3cff8080808080ff8080808080ff
    ff010b80ff0180ffff02ffff03ff05ffff01ff04ff09ffff02ff16ffff04ff02
    ffff04ff0dffff04ff0bff808080808080ffff010b80ff0180ffff0bff2affff
    0bff3cff3880ffff0bff2affff0bff2affff0bff3cff1280ff0580ffff0bff2a
    ffff02ff3affff04ff02ffff04ff07ffff04ffff0bff3cff3c80ff8080808080
    ffff0bff3cff8080808080ff02ffff03ffff07ff0580ffff01ff0bffff0102ff
    ff02ff3effff04ff02ffff04ff09ff80808080ffff02ff3effff04ff02ffff04
    ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080
    "
);

pub const P2_DELEGATED_SINGLETON_PUZZLE_HASH: TreeHash = TreeHash::new(hex!(
    "2cadfbf73f1ff120d708ad2fefad1c78eefb8d874231bc87eac7c2df5eeb904a"
));

#[cfg(test)]
mod tests {
    use super::*;

    use crate::assert_puzzle_hash;

    #[test]
    fn test_puzzle_hash() -> anyhow::Result<()> {
        assert_puzzle_hash!(P2_DELEGATED_SINGLETON_PUZZLE => P2_DELEGATED_SINGLETON_PUZZLE_HASH);
        Ok(())
    }
}