chia_sdk_driver/primitives/action_layer/
state_scheduler_info.rs1use chia_protocol::Bytes32;
2use chia_puzzle_types::Memos;
3use chia_puzzle_types::singleton::{LauncherSolution, SingletonArgs, SingletonStruct};
4use chia_sdk_types::Condition;
5use chia_sdk_types::puzzles::StateSchedulerLayerArgs;
6use clvm_traits::{FromClvm, ToClvm, clvm_quote};
7use clvm_utils::{ToTreeHash, TreeHash};
8use clvmr::{Allocator, NodePtr};
9
10use crate::{DriverError, SingletonLayer, StateSchedulerLayer};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct StateSchedulerInfo<S> {
14 pub launcher_id: Bytes32,
15
16 pub receiver_singleton_launcher_id: Bytes32,
17 pub state_schedule: Vec<(u32, S)>, pub generation: usize,
19 pub final_puzzle_hash: Bytes32,
20}
21
22impl<S> StateSchedulerInfo<S>
23where
24 S: ToTreeHash + Clone,
25{
26 pub fn new(
27 launcher_id: Bytes32,
28 receiver_singleton_launcher_id: Bytes32,
29 state_schedule: Vec<(u32, S)>,
30 generation: usize,
31 final_puzzle_hash: Bytes32,
32 ) -> Self {
33 Self {
34 launcher_id,
35 receiver_singleton_launcher_id,
36 state_schedule,
37 generation,
38 final_puzzle_hash,
39 }
40 }
41
42 #[must_use]
43 pub fn with_generation(&self, generation: usize) -> Self {
44 Self {
45 generation,
46 ..self.clone()
47 }
48 }
49
50 pub fn inner_puzzle_hash_for(
51 &self,
52 next_puzzle_hash: Bytes32,
53 required_block_height: u32,
54 new_state: &S,
55 ) -> TreeHash {
56 let message: Bytes32 = new_state.tree_hash().into();
57
58 StateSchedulerLayerArgs::curry_tree_hash(
59 SingletonStruct::new(self.receiver_singleton_launcher_id)
60 .tree_hash()
61 .into(),
62 &message,
63 &clvm_quote!(vec![
64 Condition::<()>::create_coin(next_puzzle_hash, 1, Memos::None),
65 Condition::assert_height_absolute(required_block_height),
66 ]),
67 )
68 }
69
70 pub fn inner_puzzle_hash_for_generation(&self, generation: usize) -> TreeHash {
71 if generation >= self.state_schedule.len() {
72 return self.final_puzzle_hash.into();
73 }
74
75 let mut inner_puzzle_hash: TreeHash = self.final_puzzle_hash.into();
76
77 let mut i = self.state_schedule.len();
78 while i > generation {
79 inner_puzzle_hash = self.inner_puzzle_hash_for(
80 inner_puzzle_hash.into(),
81 self.state_schedule[i - 1].0,
82 &self.state_schedule[i - 1].1,
83 );
84
85 i -= 1;
86 }
87
88 inner_puzzle_hash
89 }
90
91 pub fn inner_puzzle_hash(&self) -> TreeHash {
92 self.inner_puzzle_hash_for_generation(self.generation)
93 }
94
95 pub fn into_layers(self) -> SingletonLayer<StateSchedulerLayer> {
96 let (required_block_height, new_state) = self.state_schedule[self.generation].clone();
97
98 SingletonLayer::new(
99 self.launcher_id,
100 StateSchedulerLayer::new(
101 SingletonStruct::new(self.receiver_singleton_launcher_id)
102 .tree_hash()
103 .into(),
104 new_state.tree_hash().into(),
105 required_block_height,
106 self.inner_puzzle_hash_for_generation(self.generation + 1)
107 .into(),
108 ),
109 )
110 }
111
112 pub fn from_launcher_solution<H>(
113 allocator: &mut Allocator,
114 laucher_solution: LauncherSolution<NodePtr>,
115 ) -> Result<Option<(Self, H)>, DriverError>
116 where
117 S: FromClvm<Allocator>,
118 H: FromClvm<Allocator>,
119 {
120 let hints = StateSchedulerLauncherHints::<S, H>::from_clvm(
121 allocator,
122 laucher_solution.key_value_list,
123 )?;
124
125 let candidate = Self::new(
126 hints.my_launcher_id,
127 hints.receiver_singleton_launcher_id,
128 hints.state_schedule,
129 0,
130 hints.final_puzzle_hash,
131 );
132
133 let predicted_inner_puzzle_hash = candidate.inner_puzzle_hash();
134 let predicted_puzzle_hash =
135 SingletonArgs::curry_tree_hash(hints.my_launcher_id, predicted_inner_puzzle_hash);
136
137 if laucher_solution.amount == 1
138 && laucher_solution.singleton_puzzle_hash == predicted_puzzle_hash.into()
139 {
140 Ok(Some((candidate, hints.final_puzzle_hash_hints)))
141 } else {
142 Ok(None)
143 }
144 }
145
146 pub fn to_hints<H>(&self, final_puzzle_hash_hints: H) -> StateSchedulerLauncherHints<S, H> {
147 StateSchedulerLauncherHints {
148 my_launcher_id: self.launcher_id,
149 receiver_singleton_launcher_id: self.receiver_singleton_launcher_id,
150 final_puzzle_hash: self.final_puzzle_hash,
151 state_schedule: self.state_schedule.clone(),
152 final_puzzle_hash_hints,
153 }
154 }
155}
156
157#[derive(ToClvm, FromClvm, Debug, Clone, PartialEq, Eq)]
158#[clvm(curry)]
159pub struct StateSchedulerLauncherHints<S, H> {
160 pub my_launcher_id: Bytes32,
161 pub receiver_singleton_launcher_id: Bytes32,
162 pub final_puzzle_hash: Bytes32,
163 pub state_schedule: Vec<(u32, S)>,
164 #[clvm(rest)]
165 pub final_puzzle_hash_hints: H,
166}