cuprate_types/
output_cache.rs1use indexmap::{IndexMap, IndexSet};
2use monero_oxide::{io::CompressedPoint, transaction::Transaction};
3
4use cuprate_helper::{cast::u64_to_usize, crypto::compute_zero_commitment};
5
6use crate::{OutputOnChain, VerifiedBlockInformation};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct OutputCache {
11 cached_outputs: IndexMap<u64, IndexMap<u64, OutputOnChain>>,
13 number_of_outputs: IndexMap<u64, u64>,
15 wanted_outputs: IndexMap<u64, IndexSet<u64>>,
17}
18
19impl OutputCache {
20 pub const fn new(
22 cached_outputs: IndexMap<u64, IndexMap<u64, OutputOnChain>>,
23 number_of_outputs: IndexMap<u64, u64>,
24 wanted_outputs: IndexMap<u64, IndexSet<u64>>,
25 ) -> Self {
26 Self {
27 cached_outputs,
28 number_of_outputs,
29 wanted_outputs,
30 }
31 }
32
33 pub const fn cached_outputs(&self) -> &IndexMap<u64, IndexMap<u64, OutputOnChain>> {
40 &self.cached_outputs
41 }
42
43 pub fn number_outs_with_amount(&self, amount: u64) -> usize {
51 u64_to_usize(
52 self.number_of_outputs
53 .get(&amount)
54 .copied()
55 .unwrap_or_default(),
56 )
57 }
58
59 pub fn get_output(&self, amount: u64, index: u64) -> Option<&OutputOnChain> {
61 self.cached_outputs
62 .get(&amount)
63 .and_then(|map| map.get(&index))
64 }
65
66 fn add_tx<const MINER_TX: bool>(&mut self, height: usize, tx: &Transaction) {
68 for (i, out) in tx.prefix().outputs.iter().enumerate() {
69 let amount = if MINER_TX && tx.version() == 2 {
70 0
71 } else {
72 out.amount.unwrap_or_default()
73 };
74
75 let Some(outputs_with_amount) = self.number_of_outputs.get_mut(&amount) else {
76 continue;
77 };
78
79 let amount_index_of_out = *outputs_with_amount;
80 *outputs_with_amount += 1;
81
82 if let Some(set) = self.wanted_outputs.get_mut(&amount) {
83 if set.swap_remove(&amount_index_of_out) {
84 self.cached_outputs.entry(amount).or_default().insert(
85 amount_index_of_out,
86 OutputOnChain {
87 height,
88 time_lock: tx.prefix().additional_timelock,
89 key: out.key,
90 commitment: get_output_commitment(tx, i),
91 txid: None,
92 },
93 );
94 }
95 }
96 }
97 }
98
99 pub fn add_block_to_cache(&mut self, block: &VerifiedBlockInformation) {
104 self.add_tx::<true>(block.height, &block.block.miner_transaction);
105
106 for tx in &block.txs {
107 self.add_tx::<false>(block.height, &tx.tx);
108 }
109 }
110}
111
112fn get_output_commitment(tx: &Transaction, i: usize) -> CompressedPoint {
114 match tx {
115 Transaction::V1 { prefix, .. } => {
116 compute_zero_commitment(prefix.outputs[i].amount.unwrap_or_default())
117 }
118 Transaction::V2 { prefix, proofs } => {
119 let Some(proofs) = proofs else {
120 return compute_zero_commitment(prefix.outputs[i].amount.unwrap_or_default());
121 };
122
123 proofs.base.commitments[i]
124 }
125 }
126}