chik_sdk_test/
announcements.rs

1use chik_protocol::{Bytes, Bytes32, CoinSpend};
2use chik_sdk_types::{
3    announcement_id,
4    conditions::{
5        AssertCoinAnnouncement, AssertPuzzleAnnouncement, CreateCoinAnnouncement,
6        CreatePuzzleAnnouncement,
7    },
8};
9use klvm_traits::{FromKlvm, ToKlvm};
10use klvmr::{reduction::Reduction, run_program, Allocator, ChikDialect, NodePtr};
11
12#[derive(Debug, Default, Clone)]
13pub struct Announcements {
14    pub created_coin: Vec<Bytes>,
15    pub asserted_coin: Vec<Bytes32>,
16    pub created_puzzle: Vec<Bytes>,
17    pub asserted_puzzle: Vec<Bytes32>,
18}
19
20/// Print the announcements that are created and asserted by a list of coin spends.
21///
22/// # Panics
23///
24/// Panics if the announcements cannot be extracted from the coin spends.
25pub fn debug_announcements(coin_spends: &[CoinSpend]) {
26    let all_announcements: Vec<Announcements> = coin_spends
27        .iter()
28        .map(|coin_spend| {
29            announcements_for_spend(coin_spend).expect("could not extract announcements")
30        })
31        .collect();
32
33    let mut should_panic = false;
34
35    for (i, announcements) in all_announcements.iter().enumerate() {
36        for &asserted_coin in &announcements.asserted_coin {
37            let Some(created_index) = all_announcements.iter().enumerate().position(|(i, a)| {
38                a.created_coin.iter().any(|message| {
39                    asserted_coin == announcement_id(coin_spends[i].coin.coin_id(), message.clone())
40                })
41            }) else {
42                println!(
43                    "spend {i} asserted unknown coin announcement {}",
44                    hex::encode(&asserted_coin[0..4])
45                );
46                should_panic = true;
47                continue;
48            };
49
50            println!(
51                "spend {i} asserted coin announcement created by spend {created_index}: {}",
52                hex::encode(&asserted_coin[0..4])
53            );
54        }
55
56        for &asserted_puzzle in &announcements.asserted_puzzle {
57            let Some(created_index) = all_announcements.iter().enumerate().position(|(i, a)| {
58                a.created_puzzle.iter().any(|message| {
59                    asserted_puzzle
60                        == announcement_id(coin_spends[i].coin.puzzle_hash, message.clone())
61                })
62            }) else {
63                println!(
64                    "spend {i} asserted unknown puzzle announcement {}",
65                    hex::encode(&asserted_puzzle[0..4])
66                );
67                should_panic = true;
68                continue;
69            };
70
71            println!(
72                "spend {i} asserted puzzle announcement created by spend {created_index}: {}",
73                hex::encode(&asserted_puzzle[0..4])
74            );
75        }
76
77        for created_coin in &announcements.created_coin {
78            let asserted = all_announcements.iter().enumerate().any(|(i, a)| {
79                a.asserted_coin.iter().any(|&id| {
80                    id == announcement_id(coin_spends[i].coin.coin_id(), created_coin.clone())
81                })
82            });
83
84            if !asserted {
85                println!(
86                    "spend {i} created coin announcement {} but it was not asserted",
87                    hex::encode(&created_coin[0..4])
88                );
89                should_panic = true;
90            }
91        }
92
93        for created_puzzle in &announcements.created_puzzle {
94            let asserted = all_announcements.iter().enumerate().any(|(i, a)| {
95                a.asserted_puzzle.iter().any(|&id| {
96                    id == announcement_id(coin_spends[i].coin.puzzle_hash, created_puzzle.clone())
97                })
98            });
99
100            if !asserted {
101                println!(
102                    "spend {i} created puzzle announcement {} but it was not asserted",
103                    hex::encode(&created_puzzle[0..4])
104                );
105                should_panic = true;
106            }
107        }
108    }
109
110    assert!(
111        !should_panic,
112        "asserted announcements do not match created announcements"
113    );
114}
115
116pub fn announcements_for_spend(coin_spend: &CoinSpend) -> anyhow::Result<Announcements> {
117    let mut announcements = Announcements::default();
118
119    let allocator = &mut Allocator::new();
120    let puzzle = coin_spend.puzzle_reveal.to_klvm(allocator)?;
121    let solution = coin_spend.solution.to_klvm(allocator)?;
122
123    let Reduction(_cost, output) = run_program(
124        allocator,
125        &ChikDialect::new(0),
126        puzzle,
127        solution,
128        11_000_000_000,
129    )?;
130
131    let conditions = Vec::<NodePtr>::from_klvm(allocator, output)?;
132
133    for condition in conditions {
134        if let Ok(condition) = CreateCoinAnnouncement::from_klvm(allocator, condition) {
135            announcements.created_coin.push(condition.message);
136        } else if let Ok(condition) = CreatePuzzleAnnouncement::from_klvm(allocator, condition) {
137            announcements.created_puzzle.push(condition.message);
138        } else if let Ok(condition) = AssertCoinAnnouncement::from_klvm(allocator, condition) {
139            announcements.asserted_coin.push(condition.announcement_id);
140        } else if let Ok(condition) = AssertPuzzleAnnouncement::from_klvm(allocator, condition) {
141            announcements
142                .asserted_puzzle
143                .push(condition.announcement_id);
144        }
145    }
146
147    Ok(announcements)
148}