chia_sdk_driver/layers/
standard_layer.rs1use chia_bls::PublicKey;
2use chia_protocol::Coin;
3use chia_puzzle_types::standard::{StandardArgs, StandardSolution};
4use chia_puzzles::P2_DELEGATED_PUZZLE_OR_HIDDEN_PUZZLE_HASH;
5use chia_sdk_types::Conditions;
6use clvm_traits::{clvm_quote, FromClvm};
7use clvm_utils::{ToTreeHash, TreeHash};
8use clvmr::{Allocator, NodePtr};
9
10use crate::{DriverError, Layer, Puzzle, Spend, SpendContext, SpendWithConditions};
11
12pub type P2DelegatedOrHiddenLayer = StandardLayer;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct StandardLayer {
25 pub synthetic_key: PublicKey,
26}
27
28impl StandardLayer {
29 pub fn new(synthetic_key: PublicKey) -> Self {
30 Self { synthetic_key }
31 }
32
33 pub fn spend(
34 &self,
35 ctx: &mut SpendContext,
36 coin: Coin,
37 conditions: Conditions,
38 ) -> Result<(), DriverError> {
39 let spend = self.spend_with_conditions(ctx, conditions)?;
40 ctx.spend(coin, spend)
41 }
42
43 pub fn delegated_inner_spend(
44 &self,
45 ctx: &mut SpendContext,
46 spend: Spend,
47 ) -> Result<Spend, DriverError> {
48 self.construct_spend(
49 ctx,
50 StandardSolution {
51 original_public_key: None,
52 delegated_puzzle: spend.puzzle,
53 solution: spend.solution,
54 },
55 )
56 }
57}
58
59impl Layer for StandardLayer {
60 type Solution = StandardSolution<NodePtr, NodePtr>;
61
62 fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
63 ctx.curry(StandardArgs::new(self.synthetic_key))
64 }
65
66 fn construct_solution(
67 &self,
68 ctx: &mut SpendContext,
69 solution: Self::Solution,
70 ) -> Result<NodePtr, DriverError> {
71 ctx.alloc(&solution)
72 }
73
74 fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
75 let Some(puzzle) = puzzle.as_curried() else {
76 return Ok(None);
77 };
78
79 if puzzle.mod_hash != P2_DELEGATED_PUZZLE_OR_HIDDEN_PUZZLE_HASH.into() {
80 return Ok(None);
81 }
82
83 let args = StandardArgs::from_clvm(allocator, puzzle.args)?;
84
85 Ok(Some(Self {
86 synthetic_key: args.synthetic_key,
87 }))
88 }
89
90 fn parse_solution(
91 allocator: &Allocator,
92 solution: NodePtr,
93 ) -> Result<Self::Solution, DriverError> {
94 Ok(StandardSolution::from_clvm(allocator, solution)?)
95 }
96}
97
98impl SpendWithConditions for StandardLayer {
99 fn spend_with_conditions(
100 &self,
101 ctx: &mut SpendContext,
102 conditions: Conditions,
103 ) -> Result<Spend, DriverError> {
104 let delegated_puzzle = ctx.alloc(&clvm_quote!(conditions))?;
105 self.construct_spend(
106 ctx,
107 StandardSolution {
108 original_public_key: None,
109 delegated_puzzle,
110 solution: NodePtr::NIL,
111 },
112 )
113 }
114}
115
116impl ToTreeHash for StandardLayer {
117 fn tree_hash(&self) -> TreeHash {
118 StandardArgs::curry_tree_hash(self.synthetic_key)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use chia_puzzle_types::Memos;
125 use chia_sdk_test::Simulator;
126
127 use super::*;
128
129 #[test]
130 fn test_flash_loan() -> anyhow::Result<()> {
131 let mut sim = Simulator::new();
132 let ctx = &mut SpendContext::new();
133 let alice = sim.bls(1);
134 let p2 = StandardLayer::new(alice.pk);
135
136 p2.spend(
137 ctx,
138 alice.coin,
139 Conditions::new().create_coin(alice.puzzle_hash, u64::MAX, Memos::None),
140 )?;
141
142 p2.spend(
143 ctx,
144 Coin::new(alice.coin.coin_id(), alice.puzzle_hash, u64::MAX),
145 Conditions::new().create_coin(alice.puzzle_hash, 1, Memos::None),
146 )?;
147
148 sim.spend_coins(ctx.take(), &[alice.sk])?;
149
150 Ok(())
151 }
152}