chia_sdk_driver/layers/action_layer/
state_scheduler_layer.rs1use 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}