chik_consensus/
solution_generator.rs

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
7/// the tuple has the Coin, puzzle-reveal and solution
8fn 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    // the generator we produce here is just a quoted list. Nothing fancy.
14    // Its format is as follows:
15    // (q . ( ( ( parent-id puzzle-reveal amount solution ) ... ) ) )
16
17    let mut spend_list = a.nil();
18    for s in spends {
19        let item = a.nil();
20        // solution
21        let solution = node_from_bytes_backrefs(a, s.2.as_ref())?;
22        let item = a.new_pair(solution, item)?;
23        // amount
24        let amount = a.new_number(s.0.amount.into())?;
25        let item = a.new_pair(amount, item)?;
26        // puzzle reveal
27        let puzzle = node_from_bytes_backrefs(a, s.1.as_ref())?;
28        let item = a.new_pair(puzzle, item)?;
29        // parent-id
30        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    // the list of spends is the first (and only) item in an outer list
37    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
43/// this function returns the number of bytes the specified
44/// number is serialized to, in KLVM serialized form
45fn 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
67/// calculate the size in bytes of a generator with no backref optimisations
68pub fn calculate_generator_length<I>(spends: I) -> usize
69where
70    I: AsRef<[CoinSpend]>,
71{
72    let mut size: usize = 5; // (q . (())) => ff01ff8080 => 5 bytes
73
74    for s in spends.as_ref() {
75        let puzzle = s.puzzle_reveal.as_ref();
76        let solution = s.solution.as_ref();
77        // Each spend has the following form:
78        // ( parent-id puzzle-reveal amount solution )
79        // parent-id is always 32 bytes + 1 byte length prefix = 33
80        // + 6 bytes for list extension
81        // coin amount is already prepended correctly in klvm_bytes_len()
82        size += 39 + puzzle.len() + klvm_bytes_len(s.coin.amount) + solution.len();
83    }
84
85    size
86}
87
88/// the tuple has the Coin, puzzle-reveal and solution
89pub 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}