action_layer_driver/
action_layer.rs1use std::marker::PhantomData;
6
7use chia::protocol::Bytes32;
8use chia_wallet_sdk::driver::{
9 ActionLayer, ActionLayerSolution, Finalizer, Layer, Spend, SpendContext,
10};
11use chia_wallet_sdk::types::puzzles::{ActionLayerArgs, DefaultFinalizer2ndCurryArgs};
12use chia_wallet_sdk::types::MerkleTree;
13use clvm_traits::{FromClvm, ToClvm};
14use clvm_utils::{ToTreeHash, TreeHash};
15use clvmr::{Allocator, NodePtr};
16
17use crate::DriverError;
18
19pub struct ActionLayerConfig<S> {
40 action_hashes: Vec<Bytes32>,
41 merkle_tree: MerkleTree,
42 hint: Bytes32,
43 _phantom: PhantomData<S>,
44}
45
46impl<S> ActionLayerConfig<S>
47where
48 S: Clone + ToClvm<Allocator> + FromClvm<Allocator> + ToTreeHash,
49{
50 pub fn new(action_hashes: Vec<Bytes32>, hint: Bytes32) -> Self {
56 let merkle_tree = MerkleTree::new(&action_hashes);
57 Self {
58 action_hashes,
59 merkle_tree,
60 hint,
61 _phantom: PhantomData,
62 }
63 }
64
65 pub fn action_hashes(&self) -> &[Bytes32] {
67 &self.action_hashes
68 }
69
70 pub fn merkle_root(&self) -> Bytes32 {
72 self.merkle_tree.root()
73 }
74
75 pub fn hint(&self) -> Bytes32 {
77 self.hint
78 }
79
80 pub fn inner_puzzle_hash(&self, state: &S) -> TreeHash {
87 let finalizer_hash = DefaultFinalizer2ndCurryArgs::curry_tree_hash(self.hint);
89
90 ActionLayerArgs::<TreeHash, TreeHash>::curry_tree_hash(
91 finalizer_hash,
92 self.merkle_tree.root(),
93 state.tree_hash(),
94 )
95 }
96
97 pub fn create_action_layer(&self, state: S) -> ActionLayer<S> {
99 ActionLayer::from_action_puzzle_hashes(
100 &self.action_hashes,
101 state,
102 Finalizer::Default { hint: self.hint },
103 )
104 }
105
106 pub fn build_action_spend(
117 &self,
118 ctx: &mut SpendContext,
119 state: S,
120 action_index: usize,
121 action_puzzle: NodePtr,
122 action_solution: NodePtr,
123 ) -> Result<(NodePtr, NodePtr), DriverError> {
124 if action_index >= self.action_hashes.len() {
126 return Err(DriverError::InvalidActionIndex {
127 index: action_index,
128 count: self.action_hashes.len(),
129 });
130 }
131
132 let action_hash = self.action_hashes[action_index];
133
134 let action_layer = self.create_action_layer(state);
136
137 let inner_puzzle = action_layer
139 .construct_puzzle(ctx)
140 .map_err(|e| DriverError::ActionLayer(format!("construct_puzzle: {:?}", e)))?;
141
142 let merkle_proof = self
144 .merkle_tree
145 .proof(action_hash)
146 .ok_or(DriverError::MerkleProofNotFound)?;
147
148 let action_layer_solution = ActionLayerSolution {
150 proofs: vec![merkle_proof],
151 action_spends: vec![Spend::new(action_puzzle, action_solution)],
152 finalizer_solution: NodePtr::NIL,
153 };
154
155 let inner_solution = action_layer
156 .construct_solution(ctx, action_layer_solution)
157 .map_err(|e| DriverError::ActionLayer(format!("construct_solution: {:?}", e)))?;
158
159 Ok((inner_puzzle, inner_solution))
160 }
161}
162
163impl<S> Clone for ActionLayerConfig<S> {
164 fn clone(&self) -> Self {
165 Self {
166 action_hashes: self.action_hashes.clone(),
167 merkle_tree: MerkleTree::new(&self.action_hashes),
168 hint: self.hint,
169 _phantom: PhantomData,
170 }
171 }
172}
173
174impl<S> std::fmt::Debug for ActionLayerConfig<S> {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 f.debug_struct("ActionLayerConfig")
177 .field("action_count", &self.action_hashes.len())
178 .field("merkle_root", &hex::encode(self.merkle_tree.root()))
179 .field("hint", &hex::encode(self.hint))
180 .finish()
181 }
182}