1use crate::tx::extract_and_verify_spell;
2use bitcoin::hashes::Hash;
3use charms_data::{App, Charms, Data, Transaction, TxId, UtxoId};
4use serde::{Deserialize, Serialize};
5use std::collections::{BTreeMap, BTreeSet};
6
7pub mod tx;
8
9pub const V0: u32 = 0u32;
11pub const V0_SPELL_VK: &str = "0x00e9398ac819e6dd281f81db3ada3fe5159c3cc40222b5ddb0e7584ed2327c5d";
13pub const V1_SPELL_VK: &str = "0x009f38f590ebca4c08c1e97b4064f39e4cd336eea4069669c5f5170a38a1ff97";
15pub const V1: u32 = 1u32;
17pub const V2: u32 = 2u32;
19pub const CURRENT_VERSION: u32 = V2;
21
22pub type NormalizedCharms = BTreeMap<usize, Data>;
25
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
28pub struct NormalizedTransaction {
29 #[serde(skip_serializing_if = "Option::is_none")]
32 pub ins: Option<Vec<UtxoId>>,
33 pub refs: BTreeSet<UtxoId>,
35 pub outs: Vec<NormalizedCharms>,
41}
42
43impl NormalizedTransaction {
44 pub fn prev_txids(&self) -> Option<BTreeSet<&TxId>> {
46 self.ins
47 .as_ref()
48 .map(|ins| ins.iter().map(|utxo_id| &utxo_id.0).collect())
49 }
50}
51
52pub type Proof = Box<[u8]>;
54
55#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
58pub struct NormalizedSpell {
59 pub version: u32,
61 pub tx: NormalizedTransaction,
63 pub app_public_inputs: BTreeMap<App, Data>,
65}
66
67pub fn prev_spells(
69 prev_txs: &Vec<bitcoin::Transaction>,
70 spell_vk: &str,
71) -> BTreeMap<TxId, (Option<NormalizedSpell>, usize)> {
72 prev_txs
73 .iter()
74 .map(|tx| {
75 let tx_id = TxId(tx.compute_txid().to_byte_array());
76 (
77 tx_id,
78 (
79 extract_and_verify_spell(tx, spell_vk)
80 .map_err(|e| {
81 eprintln!("no correct spell in tx {}: {}", tx_id, e);
82 })
83 .ok(),
84 tx.output.len(),
85 ),
86 )
87 })
88 .collect()
89}
90
91pub fn well_formed(
93 spell: &NormalizedSpell,
94 prev_spells: &BTreeMap<TxId, (Option<NormalizedSpell>, usize)>,
95) -> bool {
96 if spell.version != CURRENT_VERSION {
97 eprintln!(
98 "spell version {} is not the current version {}",
99 spell.version, CURRENT_VERSION
100 );
101 return false;
102 }
103 let created_by_prev_spells = |utxo_id: &UtxoId| -> bool {
104 prev_spells
105 .get(&utxo_id.0)
106 .and_then(|(_, num_tx_outs)| Some(utxo_id.1 as usize <= *num_tx_outs))
107 == Some(true)
108 };
109 if !spell
110 .tx
111 .outs
112 .iter()
113 .all(|n_charm| n_charm.keys().all(|i| i < &spell.app_public_inputs.len()))
114 {
115 eprintln!("charm app index higher than app_public_inputs.len()");
116 return false;
117 }
118 let Some(tx_ins) = &spell.tx.ins else {
121 eprintln!("no tx.ins");
122 return false;
123 };
124 if !tx_ins.iter().all(created_by_prev_spells)
125 || !spell.tx.refs.iter().all(created_by_prev_spells)
126 {
127 eprintln!("input or reference UTXOs are not created by prev transactions");
128 return false;
129 }
130 true
131}
132
133pub fn apps(spell: &NormalizedSpell) -> Vec<App> {
135 spell.app_public_inputs.keys().cloned().collect()
136}
137
138pub fn to_tx(
140 spell: &NormalizedSpell,
141 prev_spells: &BTreeMap<TxId, (Option<NormalizedSpell>, usize)>,
142) -> Transaction {
143 let from_utxo_id = |utxo_id: &UtxoId| -> (UtxoId, Charms) {
144 let (prev_spell_opt, _) = &prev_spells[&utxo_id.0];
145 let charms = prev_spell_opt
146 .as_ref()
147 .and_then(|prev_spell| {
148 prev_spell
149 .tx
150 .outs
151 .get(utxo_id.1 as usize)
152 .map(|n_charms| charms(prev_spell, n_charms))
153 })
154 .unwrap_or_default();
155 (utxo_id.clone(), charms)
156 };
157
158 let from_normalized_charms =
159 |n_charms: &NormalizedCharms| -> Charms { charms(spell, n_charms) };
160
161 let Some(tx_ins) = &spell.tx.ins else {
162 unreachable!("self.tx.ins MUST be Some at this point");
163 };
164 Transaction {
165 ins: tx_ins.iter().map(from_utxo_id).collect(),
166 refs: spell.tx.refs.iter().map(from_utxo_id).collect(),
167 outs: spell.tx.outs.iter().map(from_normalized_charms).collect(),
168 }
169}
170
171pub fn charms(spell: &NormalizedSpell, n_charms: &NormalizedCharms) -> Charms {
173 let apps = apps(spell);
174 n_charms
175 .iter()
176 .map(|(&i, data)| (apps[i].clone(), data.clone()))
177 .collect()
178}
179
180#[derive(Clone, Debug, Serialize, Deserialize)]
181pub struct SpellProverInput {
182 pub self_spell_vk: String,
183 pub prev_txs: Vec<bitcoin::Transaction>,
184 pub spell: NormalizedSpell,
185 pub app_contract_proofs: BTreeSet<usize>, }
188
189#[cfg(test)]
190mod test {
191 #[test]
192 fn dummy() {}
193}