1use crate::condition_sanitizers::sanitize_hash;
2use crate::sanitize_int::{sanitize_uint, SanitizedUint};
3use crate::validation_error::{first, rest, ErrorCode, ValidationErr};
4use chik_protocol::Bytes32;
5use klvmr::{Allocator, NodePtr};
6use std::sync::Arc;
7
8pub const PARENT: u8 = 0b100;
12pub const PUZZLE: u8 = 0b010;
13pub const AMOUNT: u8 = 0b001;
14pub const PUZZLEAMOUNT: u8 = 0b011;
15pub const PARENTAMOUNT: u8 = 0b101;
16pub const PARENTPUZZLE: u8 = 0b110;
17pub const COINID: u8 = 0b111;
18
19#[derive(Debug)]
20pub enum SpendId {
21 OwnedCoinId(Arc<Bytes32>),
22 CoinId(NodePtr),
23 Parent(NodePtr),
24 Puzzle(NodePtr),
25 Amount(u64),
26 PuzzleAmount(NodePtr, u64),
27 ParentAmount(NodePtr, u64),
28 ParentPuzzle(NodePtr, NodePtr),
29 None,
30}
31
32impl SpendId {
33 pub fn parse(a: &Allocator, args: &mut NodePtr, mode: u8) -> Result<SpendId, ValidationErr> {
35 if mode == COINID {
39 let coinid = sanitize_hash(a, first(a, *args)?, 32, ErrorCode::InvalidCoinId)?;
40 *args = rest(a, *args)?;
41 return Ok(Self::CoinId(coinid));
42 }
43
44 let parent = if (mode & PARENT) != 0 {
45 let parent = sanitize_hash(a, first(a, *args)?, 32, ErrorCode::InvalidParentId)?;
46 *args = rest(a, *args)?;
47 parent
48 } else {
49 NodePtr::NIL
50 };
51
52 let puzzle = if (mode & PUZZLE) != 0 {
53 let puzzle = sanitize_hash(a, first(a, *args)?, 32, ErrorCode::InvalidPuzzleHash)?;
54 *args = rest(a, *args)?;
55 puzzle
56 } else {
57 NodePtr::NIL
58 };
59
60 let amount = if (mode & AMOUNT) != 0 {
61 let amount = match sanitize_uint(a, first(a, *args)?, 8, ErrorCode::InvalidCoinAmount)?
62 {
63 SanitizedUint::PositiveOverflow => {
64 return Err(ValidationErr(*args, ErrorCode::CoinAmountExceedsMaximum));
65 }
66 SanitizedUint::NegativeOverflow => {
67 return Err(ValidationErr(*args, ErrorCode::CoinAmountNegative));
68 }
69 SanitizedUint::Ok(amount) => amount,
70 };
71 *args = rest(a, *args)?;
72 amount
73 } else {
74 0
75 };
76
77 match mode {
78 PARENT => Ok(Self::Parent(parent)),
79 PUZZLE => Ok(Self::Puzzle(puzzle)),
80 AMOUNT => Ok(Self::Amount(amount)),
81 PARENTPUZZLE => Ok(Self::ParentPuzzle(parent, puzzle)),
82 PARENTAMOUNT => Ok(Self::ParentAmount(parent, amount)),
83 PUZZLEAMOUNT => Ok(Self::PuzzleAmount(puzzle, amount)),
84 0 => Ok(Self::None),
85 _ => Err(ValidationErr(*args, ErrorCode::InvalidMessageMode)),
86 }
87 }
88
89 pub fn from_self(
90 mode: u8,
91 parent: NodePtr,
92 puzzle: NodePtr,
93 amount: u64,
94 coin_id: &Arc<Bytes32>,
95 ) -> Result<SpendId, ValidationErr> {
96 if mode == COINID {
97 return Ok(Self::OwnedCoinId(coin_id.clone()));
98 }
99
100 match mode {
101 PARENT => Ok(Self::Parent(parent)),
102 PUZZLE => Ok(Self::Puzzle(puzzle)),
103 AMOUNT => Ok(Self::Amount(amount)),
104 PARENTPUZZLE => Ok(Self::ParentPuzzle(parent, puzzle)),
105 PARENTAMOUNT => Ok(Self::ParentAmount(parent, amount)),
106 PUZZLEAMOUNT => Ok(Self::PuzzleAmount(puzzle, amount)),
107 0 => Ok(Self::None),
108 _ => Err(ValidationErr(NodePtr::NIL, ErrorCode::InvalidMessageMode)),
109 }
110 }
111
112 pub fn make_key(&self, out: &mut Vec<u8>, a: &Allocator) {
113 match self {
114 Self::OwnedCoinId(coinid) => {
115 out.push(COINID);
116 out.extend_from_slice(coinid);
117 }
118 Self::CoinId(coinid) => {
119 out.push(COINID);
120 out.extend_from_slice(a.atom(*coinid).as_ref());
121 }
122 Self::Parent(parent) => {
123 out.push(PARENT);
124 out.extend_from_slice(a.atom(*parent).as_ref());
125 }
126 Self::Puzzle(puzzle) => {
127 out.push(PUZZLE);
128 out.extend_from_slice(a.atom(*puzzle).as_ref());
129 }
130 Self::Amount(amount) => {
131 out.push(AMOUNT);
132 out.extend_from_slice(&amount.to_be_bytes());
133 }
134 Self::PuzzleAmount(puzzle, amount) => {
135 out.push(PUZZLEAMOUNT);
136 out.extend_from_slice(a.atom(*puzzle).as_ref());
137 out.extend_from_slice(&amount.to_be_bytes());
138 }
139 Self::ParentAmount(parent, amount) => {
140 out.push(PARENTAMOUNT);
141 out.extend_from_slice(a.atom(*parent).as_ref());
142 out.extend_from_slice(&amount.to_be_bytes());
143 }
144 Self::ParentPuzzle(parent, puzzle) => {
145 out.push(PARENTPUZZLE);
146 out.extend_from_slice(a.atom(*parent).as_ref());
147 out.extend_from_slice(a.atom(*puzzle).as_ref());
148 }
149 Self::None => {
150 out.push(0);
151 }
152 }
153 }
154}
155
156pub struct Message {
157 pub src: SpendId,
158 pub dst: SpendId,
159 pub msg: NodePtr,
160 pub counter: i8,
161}
162
163impl Message {
164 pub fn make_key(&self, a: &Allocator) -> Vec<u8> {
165 let mut key = Vec::<u8>::with_capacity((1 + 32 + 32) * 2 + 32);
166 self.src.make_key(&mut key, a);
167 self.dst.make_key(&mut key, a);
168 key.extend_from_slice(a.atom(self.msg).as_ref());
169 key
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use hex_literal::hex;
177 use rstest::rstest;
178
179 const BUF0: [u8; 32] = hex!("0000000000000000000000000000000000000000000000000000000000000000");
180 const BUF1: [u8; 32] = hex!("0101010101010101010101010101010101010101010101010101010101010101");
181 const BUF2: [u8; 32] = hex!("0202020202020202020202020202020202020202020202020202020202020202");
182
183 #[rstest]
184 #[case(0b000, "00")]
185 #[case(0b001, "010000000000000539")]
186 #[case(
187 0b010,
188 "020101010101010101010101010101010101010101010101010101010101010101"
189 )]
190 #[case(
191 0b100,
192 "040000000000000000000000000000000000000000000000000000000000000000"
193 )]
194 #[case(0b110, "0600000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101")]
195 #[case(
196 0b111,
197 "070202020202020202020202020202020202020202020202020202020202020202"
198 )]
199 #[case(
200 0b011,
201 "0301010101010101010101010101010101010101010101010101010101010101010000000000000539"
202 )]
203 #[case(
204 0b101,
205 "0500000000000000000000000000000000000000000000000000000000000000000000000000000539"
206 )]
207 fn test_from_self(#[case] mode: u8, #[case] expected: &str) {
208 let mut a = Allocator::new();
209 let parent = a.new_atom(&BUF0).unwrap();
210 let puzzle = a.new_atom(&BUF1).unwrap();
211 let coin_id = Arc::<Bytes32>::new(Bytes32::new(BUF2));
212 let src = SpendId::from_self(mode, parent, puzzle, 1337, &coin_id).unwrap();
213
214 let mut key = Vec::<u8>::new();
215 src.make_key(&mut key, &a);
216 assert_eq!(key, hex::decode(expected).unwrap());
217 }
218
219 #[rstest]
220 #[case(0b000, "00")]
221 #[case(0b001, "010000000000000539")]
222 #[case(
223 0b010,
224 "020101010101010101010101010101010101010101010101010101010101010101"
225 )]
226 #[case(
227 0b100,
228 "040000000000000000000000000000000000000000000000000000000000000000"
229 )]
230 #[case(0b110, "0600000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101")]
231 #[case(
232 0b111,
233 "070202020202020202020202020202020202020202020202020202020202020202"
234 )]
235 #[case(
236 0b011,
237 "0301010101010101010101010101010101010101010101010101010101010101010000000000000539"
238 )]
239 #[case(
240 0b101,
241 "0500000000000000000000000000000000000000000000000000000000000000000000000000000539"
242 )]
243 fn test_parse(#[case] mode: u8, #[case] expected: &str) {
244 let mut a = Allocator::new();
245 let mut args = NodePtr::NIL;
246 if mode == COINID {
247 let value = a.new_atom(&BUF2).unwrap();
248 args = a.new_pair(value, args).unwrap();
249 } else {
250 if (mode & AMOUNT) != 0 {
251 let value = a.new_small_number(1337).unwrap();
252 args = a.new_pair(value, args).unwrap();
253 }
254 if (mode & PUZZLE) != 0 {
255 let value = a.new_atom(&BUF1).unwrap();
256 args = a.new_pair(value, args).unwrap();
257 }
258 if (mode & PARENT) != 0 {
259 let value = a.new_atom(&BUF0).unwrap();
260 args = a.new_pair(value, args).unwrap();
261 }
262 }
263 let src = SpendId::parse(&a, &mut args, mode).unwrap();
264 assert!(a.atom_eq(args, NodePtr::NIL));
266
267 let mut key = Vec::<u8>::new();
268 src.make_key(&mut key, &a);
269 assert_eq!(key, hex::decode(expected).unwrap());
270 }
271}