chia_sdk_driver/primitives/action_layer/
state_scheduler_info.rs

1use 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)>, // block height + state
18    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}