1use crate::error::Result;
2use chik_protocol::Coin;
3use chik_protocol::CoinSpend;
4use klvmr::allocator::{Allocator, NodePtr};
5use klvmr::serde::{node_from_bytes_backrefs, node_to_bytes, node_to_bytes_backrefs};
6
7fn build_generator<BufRef, I>(a: &mut Allocator, spends: I) -> Result<NodePtr>
9where
10 BufRef: AsRef<[u8]>,
11 I: IntoIterator<Item = (Coin, BufRef, BufRef)>,
12{
13 let mut spend_list = a.nil();
18 for s in spends {
19 let item = a.nil();
20 let solution = node_from_bytes_backrefs(a, s.2.as_ref())?;
22 let item = a.new_pair(solution, item)?;
23 let amount = a.new_number(s.0.amount.into())?;
25 let item = a.new_pair(amount, item)?;
26 let puzzle = node_from_bytes_backrefs(a, s.1.as_ref())?;
28 let item = a.new_pair(puzzle, item)?;
29 let parent_id = a.new_atom(&s.0.parent_coin_info)?;
31 let item = a.new_pair(parent_id, item)?;
32
33 spend_list = a.new_pair(item, spend_list)?;
34 }
35
36 spend_list = a.new_pair(spend_list, a.nil())?;
38
39 let quote = a.new_pair(a.one(), spend_list)?;
40 Ok(quote)
41}
42
43fn klvm_bytes_len(val: u64) -> usize {
46 if val < 0x80 {
47 1
48 } else if val < 0x8000 {
49 3
50 } else if val < 0x0080_0000 {
51 4
52 } else if val < 0x8000_0000 {
53 5
54 } else if val < 0x0080_0000_0000 {
55 6
56 } else if val < 0x8000_0000_0000 {
57 7
58 } else if val < 0x0080_0000_0000_0000 {
59 8
60 } else if val < 0x8000_0000_0000_0000 {
61 9
62 } else {
63 10
64 }
65}
66
67pub fn calculate_generator_length<I>(spends: I) -> usize
69where
70 I: AsRef<[CoinSpend]>,
71{
72 let mut size: usize = 5; for s in spends.as_ref() {
75 let puzzle = s.puzzle_reveal.as_ref();
76 let solution = s.solution.as_ref();
77 size += 39 + puzzle.len() + klvm_bytes_len(s.coin.amount) + solution.len();
83 }
84
85 size
86}
87
88pub fn solution_generator<BufRef, I>(spends: I) -> Result<Vec<u8>>
90where
91 BufRef: AsRef<[u8]>,
92 I: IntoIterator<Item = (Coin, BufRef, BufRef)>,
93{
94 let mut a = Allocator::new();
95 let generator = build_generator(&mut a, spends)?;
96 Ok(node_to_bytes(&a, generator)?)
97}
98
99pub fn solution_generator_backrefs<BufRef, I>(spends: I) -> Result<Vec<u8>>
100where
101 BufRef: AsRef<[u8]>,
102 I: IntoIterator<Item = (Coin, BufRef, BufRef)>,
103{
104 let mut a = Allocator::new();
105 let generator = build_generator(&mut a, spends)?;
106 Ok(node_to_bytes_backrefs(&a, generator)?)
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use chik_protocol::Program;
113 use chik_traits::Streamable;
114 use hex_literal::hex;
115 use klvmr::{run_program, ChikDialect};
116 use rstest::rstest;
117
118 const PUZZLE1: [u8; 291] = hex!(
119 "
120 ff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff
121 1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080
122 808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff
123 04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff8080
124 8080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff
125 0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ff
126 ff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff0580
127 80ff0180ff018080ffff04ffff01b08cf5533a94afae0f4613d3ea565e47abc5
128 373415967ef5824fd009c602cb629e259908ce533c21de7fd7a68eb96c52d0ff
129 018080"
130 );
131
132 const SOLUTION1: [u8; 47] = hex!(
133 "
134 ff80ffff01ffff3dffa080115c1c71035a2cd60a49499fb9e5cb55be8d6e25e8
135 680bfc0409b7acaeffd48080ff8080"
136 );
137
138 const PUZZLE2: [u8; 99] = hex!(
139 "
140 ff01ffff33ffa01b7ab2079fa635554ad9bd4812c622e46ee3b1875a7813afba
141 127bb0cc9794f9ff887f808e9291e6c00080ffff33ffa06f184a7074c925ef86
142 88ce56941eb8929be320265f824ec7e351356cc745d38aff887f808e9291e6c0
143 008080"
144 );
145
146 const SOLUTION2: [u8; 1] = hex!("80");
147
148 fn run_generator(program: &[u8]) -> Vec<u8> {
149 let dialect = ChikDialect::new(0);
150 let mut a = Allocator::new();
151 let program = node_from_bytes_backrefs(&mut a, program).expect("node_from_bytes");
152 let env = a.nil();
153 let generator_output = run_program(&mut a, &dialect, program, env, 11_000_000_000)
154 .expect("run_program")
155 .1;
156 node_to_bytes(&a, generator_output).expect("node_to_bytes")
157 }
158
159 const EXPECTED_GENERATOR_OUTPUT: [u8; 536] = hex!(
160 "
161 ffffffa0ccd5bb71183532bff220ba46c268991a000000000000000000000000
162 00000000ffff01ffff33ffa01b7ab2079fa635554ad9bd4812c622e46ee3b187
163 5a7813afba127bb0cc9794f9ff887f808e9291e6c00080ffff33ffa06f184a70
164 74c925ef8688ce56941eb8929be320265f824ec7e351356cc745d38aff887f80
165 8e9291e6c0008080ff8900ff011d2523cd8000ff8080ffffa0ccd5bb71183532
166 bff220ba46c268991a00000000000000000000000000036840ffff02ffff01ff
167 02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1e
168 ffff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01
169 ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05
170 ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff
171 17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff
172 0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff
173 04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff01
174 8080ffff04ffff01b08cf5533a94afae0f4613d3ea565e47abc5373415967ef5
175 824fd009c602cb629e259908ce533c21de7fd7a68eb96c52d0ff018080ff8601
176 977420dc00ffff80ffff01ffff3dffa080115c1c71035a2cd60a49499fb9e5cb
177 55be8d6e25e8680bfc0409b7acaeffd48080ff8080808080"
178 );
179
180 #[test]
181 fn test_solution_generator() {
182 let coin1: Coin = Coin::new(
183 hex!("ccd5bb71183532bff220ba46c268991a00000000000000000000000000036840").into(),
184 hex!("fcc78a9e396df6ceebc217d2446bc016e0b3d5922fb32e5783ec5a85d490cfb6").into(),
185 1_750_000_000_000,
186 );
187 let coin2: Coin = Coin::new(
188 hex!("ccd5bb71183532bff220ba46c268991a00000000000000000000000000000000").into(),
189 hex!("d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc").into(),
190 18_375_000_000_000_000_000,
191 );
192
193 let spends = [
194 (coin1, PUZZLE1.as_ref(), SOLUTION1.as_ref()),
195 (coin2, PUZZLE2.as_ref(), SOLUTION2.as_ref()),
196 ];
197
198 let result = solution_generator(spends).expect("solution_generator");
199
200 assert_eq!(
201 result.len(),
202 calculate_generator_length([
203 CoinSpend {
204 coin: coin1,
205 puzzle_reveal: Program::from(PUZZLE1.as_ref()),
206 solution: Program::from(SOLUTION1.as_ref())
207 },
208 CoinSpend {
209 coin: coin2,
210 puzzle_reveal: Program::from(PUZZLE2.as_ref()),
211 solution: Program::from(SOLUTION2.as_ref())
212 },
213 ])
214 );
215
216 assert_eq!(
217 result,
218 hex!(
219 "
220 ff01ffffffa0
221 ccd5bb71183532bff220ba46c268991a00000000000000000000000000000000
222
223 ff
224
225 ff01ffff33ffa01b7ab2079fa635554ad9bd4812c622e46ee3b1875a7813afba
226 127bb0cc9794f9ff887f808e9291e6c00080ffff33ffa06f184a7074c925ef86
227 88ce56941eb8929be320265f824ec7e351356cc745d38aff887f808e9291e6c0
228 008080
229
230 ff8900ff011d2523cd8000ff
231
232 80
233
234 80ffffa0
235
236 ccd5bb71183532bff220ba46c268991a00000000000000000000000000036840
237
238 ff
239
240 ff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff
241 1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080
242 808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff
243 04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff8080
244 8080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff
245 0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ff
246 ff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff0580
247 80ff0180ff018080ffff04ffff01b08cf5533a94afae0f4613d3ea565e47abc5
248 373415967ef5824fd009c602cb629e259908ce533c21de7fd7a68eb96c52d0ff
249 018080
250
251 ff8601977420dc00ff
252
253 ff80ffff01ffff3dffa080115c1c71035a2cd60a49499fb9e5cb55be8d6e25e8
254 680bfc0409b7acaeffd48080ff8080
255
256 808080"
257 )
258 );
259
260 let generator_output = run_generator(&result);
261 assert_eq!(generator_output, EXPECTED_GENERATOR_OUTPUT);
262
263 let spends = [(coin2, PUZZLE2.as_ref(), SOLUTION2.as_ref())];
264 let result = solution_generator(spends).expect("solution_generator");
265
266 assert_eq!(
267 result.len(),
268 calculate_generator_length([CoinSpend {
269 coin: coin2,
270 puzzle_reveal: Program::from(PUZZLE2.as_ref()),
271 solution: Program::from(SOLUTION2.as_ref())
272 },])
273 );
274
275 assert_eq!(
276 result,
277 hex!(
278 "
279 ff01ffffffa0
280 ccd5bb71183532bff220ba46c268991a00000000000000000000000000000000
281
282 ff
283
284 ff01ffff33ffa01b7ab2079fa635554ad9bd4812c622e46ee3b1875a7813afba
285 127bb0cc9794f9ff887f808e9291e6c00080ffff33ffa06f184a7074c925ef86
286 88ce56941eb8929be320265f824ec7e351356cc745d38aff887f808e9291e6c0
287 008080
288
289 ff8900ff011d2523cd8000ff
290
291 80
292
293 808080"
294 )
295 );
296 }
297
298 #[test]
299 fn test_length_calculator() {
300 let mut spends: Vec<(Coin, &[u8], &[u8])> = Vec::new();
301 let mut coin_spends = Vec::<CoinSpend>::new();
302 for i in [
303 0,
304 1,
305 128,
306 129,
307 256,
308 257,
309 4_294_967_296,
310 4_294_967_297,
311 0x8000_0001,
312 0x8000_0000_0001,
313 0x8000_0000_0000_0001,
314 ] {
315 let coin: Coin = Coin::new(
316 hex!("ccd5bb71183532bff220ba46c268991a00000000000000000000000000036840").into(),
317 hex!("fcc78a9e396df6ceebc217d2446bc016e0b3d5922fb32e5783ec5a85d490cfb6").into(),
318 i,
319 );
320 spends.push((coin, PUZZLE1.as_ref(), SOLUTION1.as_ref()));
321 coin_spends.push(CoinSpend {
322 coin,
323 puzzle_reveal: Program::from(PUZZLE1.as_ref()),
324 solution: Program::from(SOLUTION1.as_ref()),
325 });
326 let result = solution_generator(spends.clone()).expect("solution_generator");
327
328 assert_eq!(result.len(), calculate_generator_length(&coin_spends));
329 }
330 }
331
332 #[rstest]
333 #[case(hex!("f800000000").as_ref(), SOLUTION1.as_ref())]
334 #[case(hex!("fffffe0000ff41ff013a").as_ref(), SOLUTION1.as_ref())]
335 #[case(PUZZLE1.as_ref(), hex!("00").as_ref())]
336 fn test_length_calculator_edge_case(#[case] puzzle: &[u8], #[case] solution: &[u8]) {
337 let mut spends: Vec<(Coin, &[u8], &[u8])> = Vec::new();
338 let mut coin_spends = Vec::<CoinSpend>::new();
339 let mut a = Allocator::new();
340 let mut discrepancy: i64 = 0;
341
342 let coin: Coin = Coin::new(
343 hex!("ccd5bb71183532bff220ba46c268991a00000000000000000000000000036840").into(),
344 hex!("fcc78a9e396df6ceebc217d2446bc016e0b3d5922fb32e5783ec5a85d490cfb6").into(),
345 100,
346 );
347 spends.push((coin, puzzle, solution));
348 coin_spends.push(CoinSpend {
349 coin,
350 puzzle_reveal: Program::from_bytes(puzzle).expect("puzzle_reveal"),
351 solution: Program::from_bytes(solution).expect("solution"),
352 });
353 let node = node_from_bytes_backrefs(&mut a, puzzle).expect("puzzle");
354 let puz = node_to_bytes(&a, node).expect("bytes");
355 discrepancy += puzzle.len() as i64 - puz.len() as i64;
356 let node = node_from_bytes_backrefs(&mut a, solution).expect("solution");
357 let sol = node_to_bytes(&a, node).expect("bytes");
358 discrepancy += solution.len() as i64 - sol.len() as i64;
359 let result = solution_generator(spends.clone()).expect("solution_generator");
360
361 assert_eq!(
362 result.len() as i64,
363 calculate_generator_length(&coin_spends) as i64 - discrepancy
364 );
365 }
366
367 #[test]
368 fn test_solution_generator_backre() {
369 let coin1: Coin = Coin::new(
370 hex!("ccd5bb71183532bff220ba46c268991a00000000000000000000000000036840").into(),
371 hex!("fcc78a9e396df6ceebc217d2446bc016e0b3d5922fb32e5783ec5a85d490cfb6").into(),
372 1_750_000_000_000,
373 );
374 let coin2: Coin = Coin::new(
375 hex!("ccd5bb71183532bff220ba46c268991a00000000000000000000000000000000").into(),
376 hex!("d23da14695a188ae5708dd152263c4db883eb27edeb936178d4d988b8f3ce5fc").into(),
377 18_375_000_000_000_000_000,
378 );
379
380 let result = solution_generator_backrefs([
381 (coin1, PUZZLE1.as_ref(), SOLUTION1.as_ref()),
382 (coin2, PUZZLE2.as_ref(), SOLUTION2.as_ref()),
383 ])
384 .expect("solution_generator");
385
386 assert_eq!(
387 result,
388 hex!(
389 "
390 ff01ffffffa0
391
392 ccd5bb71183532bff220ba46c268991a00000000000000000000000000000000
393
394 ff
395
396 ff01ffff33ffa01b7ab2079fa635554ad9bd4812c622e46ee3b1875a7813afba
397 127bb0cc9794f9ff887f808e9291e6c00080ffff33ffa06f184a7074c925ef86
398 88ce56941eb8929be320265f824ec7e351356cc745d38a
399
400 fe3b
401
402 80ff8900ff011d2523cd8000ff8080ffffa0
403
404 ccd5bb71183532bff220ba46c268991a00000000000000000000000000036840
405
406 ff
407
408 ff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff
409 1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080
410 808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff
411 04ffff04ff05ffff04ff
412
413 fe8401
414
415 6b6b7fff80808080ff
416
417 fe820d
418
419 b78080
420
421 ff0180
422
423 ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff
424 06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff
425 8080808080ffff01ff0bffff0101
426
427 ff0580
428
429 80ff0180
430
431 ff0180
432
433 80ffff04ffff01b08cf5533a94afae0f4613d3ea565e47abc5373415967ef582
434 4fd009c602cb629e259908ce533c21de7fd7a68eb96c52d0
435
436 ff0180
437
438 80ff8601977420dc00ffff80ffff01ffff3dffa080115c1c71035a2cd60a4949
439 9fb9e5cb55be8d6e25e8680bfc0409b7acaeffd48080ff8080808080"
440 )
441 );
442 let generator_output = run_generator(&result);
443 assert_eq!(generator_output, EXPECTED_GENERATOR_OUTPUT);
444 }
445
446 #[rstest]
447 #[case(0)]
448 #[case(1)]
449 #[case(0x7f)]
450 #[case(0x80)]
451 #[case(0xff)]
452 #[case(0x7fff)]
453 #[case(0x8000)]
454 #[case(0xffff)]
455 #[case(0x7f_ffff)]
456 #[case(0x80_0000)]
457 #[case(0xff_ffff)]
458 #[case(0x7fff_ffff)]
459 #[case(0x8000_0000)]
460 #[case(0xffff_ffff)]
461 #[case(0x7f_ffff_ffff)]
462 #[case(0x80_0000_0000)]
463 #[case(0xff_ffff_ffff)]
464 #[case(0x7fff_ffff_ffff)]
465 #[case(0x8000_0000_0000)]
466 #[case(0xffff_ffff_ffff)]
467 #[case(0x7f_ffff_ffff_ffff)]
468 #[case(0x80_0000_0000_0000)]
469 #[case(0xff_ffff_ffff_ffff)]
470 #[case(0x7fff_ffff_ffff_ffff)]
471 #[case(0x8000_0000_0000_0000)]
472 #[case(0xffff_ffff_ffff_ffff)]
473 #[case(0x7)]
474 #[case(0x8)]
475 #[case(0xf)]
476 #[case(0x7ff)]
477 #[case(0x800)]
478 #[case(0xfff)]
479 #[case(0x7ffff)]
480 #[case(0x80000)]
481 #[case(0xfffff)]
482 #[case(0x7ff_ffff)]
483 #[case(0x800_0000)]
484 #[case(0xfff_ffff)]
485 #[case(0x7_ffff_ffff)]
486 #[case(0x8_0000_0000)]
487 #[case(0xf_ffff_ffff)]
488 #[case(0x7ff_ffff_ffff)]
489 #[case(0x800_0000_0000)]
490 #[case(0xfff_ffff_ffff)]
491 #[case(0x7_ffff_ffff_ffff)]
492 #[case(0x8_0000_0000_0000)]
493 #[case(0xf_ffff_ffff_ffff)]
494 #[case(0x7ff_ffff_ffff_ffff)]
495 #[case(0x800_0000_0000_0000)]
496 #[case(0xfff_ffff_ffff_ffff)]
497 fn test_klvm_bytes_len(#[case] n: u64) {
498 let len = klvm_bytes_len(n);
499 let mut a = Allocator::new();
500 let atom = a.new_number(n.into()).expect("new_number");
501 let bytes = node_to_bytes(&a, atom).expect("node_to_bytes");
502 assert_eq!(bytes.len(), len);
503 }
504}