chia_sdk_driver/
spend_context.rs1use std::{
2 collections::HashMap,
3 ops::{Deref, DerefMut},
4};
5
6use chia_protocol::{Bytes32, Coin, CoinSpend, Program};
7use chia_sdk_types::{Conditions, Mod, conditions::Memos, run_puzzle};
8use clvm_traits::{FromClvm, ToClvm, clvm_quote};
9use clvm_utils::{CurriedProgram, TreeHash, tree_hash};
10use clvmr::{
11 Allocator, NodePtr,
12 allocator::Checkpoint,
13 serde::{node_from_bytes, node_to_bytes, node_to_bytes_backrefs},
14};
15
16use crate::{DriverError, HashedPtr, Spend};
17
18#[derive(Debug, Default)]
21pub struct SpendContext {
22 allocator: Allocator,
23 puzzles: HashMap<TreeHash, NodePtr>,
24 coin_spends: Vec<CoinSpend>,
25}
26
27impl SpendContext {
28 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn reset(&mut self, checkpoint: &Checkpoint) {
33 self.allocator.restore_checkpoint(checkpoint);
34 self.puzzles.clear();
35 self.coin_spends.clear();
36 }
37
38 pub fn iter(&self) -> impl Iterator<Item = &CoinSpend> {
39 self.coin_spends.iter()
40 }
41
42 pub fn take(&mut self) -> Vec<CoinSpend> {
44 std::mem::take(&mut self.coin_spends)
45 }
46
47 pub fn insert(&mut self, coin_spend: CoinSpend) {
49 self.coin_spends.push(coin_spend);
50 }
51
52 pub fn spend(&mut self, coin: Coin, spend: Spend) -> Result<(), DriverError> {
54 let puzzle_reveal = self.serialize(&spend.puzzle)?;
55 let solution = self.serialize(&spend.solution)?;
56 self.insert(CoinSpend::new(coin, puzzle_reveal, solution));
57 Ok(())
58 }
59
60 pub fn alloc<T>(&mut self, value: &T) -> Result<NodePtr, DriverError>
62 where
63 T: ToClvm<Allocator>,
64 {
65 Ok(value.to_clvm(&mut self.allocator)?)
66 }
67
68 pub fn alloc_hashed<T>(&mut self, value: &T) -> Result<HashedPtr, DriverError>
70 where
71 T: ToClvm<Allocator>,
72 {
73 let ptr = value.to_clvm(&mut self.allocator)?;
74 Ok(HashedPtr::from_ptr(self, ptr))
75 }
76
77 pub fn extract<T>(&self, ptr: NodePtr) -> Result<T, DriverError>
79 where
80 T: FromClvm<Allocator>,
81 {
82 Ok(T::from_clvm(&self.allocator, ptr)?)
83 }
84
85 pub fn tree_hash(&self, ptr: NodePtr) -> TreeHash {
87 tree_hash(&self.allocator, ptr)
88 }
89
90 pub fn run(&mut self, puzzle: NodePtr, solution: NodePtr) -> Result<NodePtr, DriverError> {
92 Ok(run_puzzle(&mut self.allocator, puzzle, solution)?)
93 }
94
95 pub fn serialize<T>(&mut self, value: &T) -> Result<Program, DriverError>
97 where
98 T: ToClvm<Allocator>,
99 {
100 let ptr = value.to_clvm(&mut self.allocator)?;
101 Ok(node_to_bytes(&self.allocator, ptr)?.into())
102 }
103
104 pub fn serialize_with_backrefs<T>(&mut self, value: &T) -> Result<Program, DriverError>
106 where
107 T: ToClvm<Allocator>,
108 {
109 let ptr = value.to_clvm(&mut self.allocator)?;
110 Ok(node_to_bytes_backrefs(&self.allocator, ptr)?.into())
111 }
112
113 pub fn memos<T>(&mut self, value: &T) -> Result<Memos<NodePtr>, DriverError>
114 where
115 T: ToClvm<Allocator>,
116 {
117 Ok(Memos::Some(self.alloc(value)?))
118 }
119
120 pub fn hint(&mut self, hint: Bytes32) -> Result<Memos<NodePtr>, DriverError> {
121 self.memos(&[hint])
122 }
123
124 pub fn alloc_mod<T>(&mut self) -> Result<NodePtr, DriverError>
125 where
126 T: Mod,
127 {
128 self.puzzle(T::mod_hash(), T::mod_reveal().as_ref())
129 }
130
131 pub fn curry<T>(&mut self, args: T) -> Result<NodePtr, DriverError>
132 where
133 T: Mod + ToClvm<Allocator>,
134 {
135 let mod_ptr = self.alloc_mod::<T>()?;
136 self.alloc(&CurriedProgram {
137 program: mod_ptr,
138 args,
139 })
140 }
141
142 pub fn get_puzzle(&self, puzzle_hash: &TreeHash) -> Option<NodePtr> {
143 self.puzzles.get(puzzle_hash).copied()
144 }
145
146 pub fn puzzle(
147 &mut self,
148 puzzle_hash: TreeHash,
149 puzzle_bytes: &[u8],
150 ) -> Result<NodePtr, DriverError> {
151 if let Some(puzzle) = self.puzzles.get(&puzzle_hash) {
152 Ok(*puzzle)
153 } else {
154 let puzzle = node_from_bytes(&mut self.allocator, puzzle_bytes)?;
155 self.puzzles.insert(puzzle_hash, puzzle);
156 Ok(puzzle)
157 }
158 }
159
160 pub fn delegated_spend(&mut self, conditions: Conditions) -> Result<Spend, DriverError> {
161 let puzzle = self.alloc(&clvm_quote!(conditions))?;
162 Ok(Spend::new(puzzle, NodePtr::NIL))
163 }
164}
165
166impl Deref for SpendContext {
167 type Target = Allocator;
168
169 fn deref(&self) -> &Self::Target {
170 &self.allocator
171 }
172}
173
174impl DerefMut for SpendContext {
175 fn deref_mut(&mut self) -> &mut Self::Target {
176 &mut self.allocator
177 }
178}
179
180impl IntoIterator for SpendContext {
181 type Item = CoinSpend;
182 type IntoIter = std::vec::IntoIter<Self::Item>;
183
184 fn into_iter(self) -> Self::IntoIter {
185 self.coin_spends.into_iter()
186 }
187}
188
189impl From<Allocator> for SpendContext {
190 fn from(allocator: Allocator) -> Self {
191 Self {
192 allocator,
193 puzzles: HashMap::new(),
194 coin_spends: Vec::new(),
195 }
196 }
197}