chia_sdk_driver/layers/action_layer/
state_scheduler_layer.rs

1use chia_protocol::Bytes32;
2use chia_puzzle_types::Memos;
3use chia_puzzles::SINGLETON_TOP_LAYER_V1_1_HASH;
4use chia_sdk_types::{
5    puzzles::{StateSchedulerLayerArgs, StateSchedulerLayerSolution, STATE_SCHEDULER_PUZZLE_HASH},
6    Condition, Conditions,
7};
8use clvm_traits::{clvm_quote, FromClvm};
9use clvmr::{Allocator, NodePtr};
10
11use crate::{DriverError, Layer, Puzzle, SpendContext};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct StateSchedulerLayer {
15    pub receiver_singleton_struct_hash: Bytes32,
16    pub new_state_hash: Bytes32,
17    pub required_block_height: u32,
18    pub new_puzzle_hash: Bytes32,
19}
20
21impl StateSchedulerLayer {
22    pub fn new(
23        receiver_singleton_struct_hash: Bytes32,
24        new_state_hash: Bytes32,
25        required_block_height: u32,
26        new_puzzle_hash: Bytes32,
27    ) -> Self {
28        Self {
29            receiver_singleton_struct_hash,
30            new_state_hash,
31            required_block_height,
32            new_puzzle_hash,
33        }
34    }
35}
36
37impl Layer for StateSchedulerLayer {
38    type Solution = StateSchedulerLayerSolution<()>;
39
40    fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
41        let Some(puzzle) = puzzle.as_curried() else {
42            return Ok(None);
43        };
44
45        if puzzle.mod_hash != STATE_SCHEDULER_PUZZLE_HASH {
46            return Ok(None);
47        }
48
49        let args = StateSchedulerLayerArgs::<Bytes32, NodePtr>::from_clvm(allocator, puzzle.args)?;
50
51        if args.singleton_mod_hash != SINGLETON_TOP_LAYER_V1_1_HASH.into() {
52            return Err(DriverError::NonStandardLayer);
53        }
54
55        let conditions = Conditions::<NodePtr>::from_clvm(allocator, args.inner_puzzle)?;
56        let (
57            Some(Condition::AssertHeightAbsolute(assert_height_condition)),
58            Some(Condition::CreateCoin(create_coin_condition)),
59        ) = conditions
60            .into_iter()
61            .fold(
62                (None, None),
63                |(assert_height, create_coin), cond| match cond {
64                    Condition::AssertHeightAbsolute(_) if assert_height.is_none() => {
65                        (Some(cond), create_coin)
66                    }
67                    Condition::CreateCoin(_) if create_coin.is_none() => {
68                        (assert_height, Some(cond))
69                    }
70                    _ => (assert_height, create_coin),
71                },
72            )
73        else {
74            return Err(DriverError::NonStandardLayer);
75        };
76
77        Ok(Some(Self {
78            receiver_singleton_struct_hash: args.receiver_singleton_struct_hash,
79            new_state_hash: args.message,
80            required_block_height: assert_height_condition.height,
81            new_puzzle_hash: create_coin_condition.puzzle_hash,
82        }))
83    }
84
85    fn parse_solution(
86        allocator: &Allocator,
87        solution: NodePtr,
88    ) -> Result<Self::Solution, DriverError> {
89        StateSchedulerLayerSolution::from_clvm(allocator, solution).map_err(DriverError::FromClvm)
90    }
91
92    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
93        let base_conditions = Conditions::new()
94            .create_coin(self.new_puzzle_hash, 1, Memos::None)
95            .assert_height_absolute(self.required_block_height);
96
97        let inner_puzzle = ctx.alloc(&clvm_quote!(base_conditions))?;
98
99        ctx.curry(StateSchedulerLayerArgs::<Bytes32, NodePtr> {
100            singleton_mod_hash: SINGLETON_TOP_LAYER_V1_1_HASH.into(),
101            receiver_singleton_struct_hash: self.receiver_singleton_struct_hash,
102            message: self.new_state_hash,
103            inner_puzzle,
104        })
105    }
106
107    fn construct_solution(
108        &self,
109        ctx: &mut SpendContext,
110        solution: Self::Solution,
111    ) -> Result<NodePtr, DriverError> {
112        ctx.alloc(&solution)
113    }
114}