aleo_rust/program/helpers/
records.rs1use super::*;
18use snarkvm_console::account::Group;
19
20#[derive(Clone)]
22pub struct RecordFinder<N: Network> {
23 api_client: AleoAPIClient<N>,
24}
25
26impl<N: Network> RecordFinder<N> {
27 pub fn new(api_client: AleoAPIClient<N>) -> Self {
28 Self { api_client }
29 }
30
31 #[allow(clippy::type_complexity)]
36 pub fn find_amount_and_fee_records(
37 &self,
38 amount: u64,
39 fee: u64,
40 private_key: &PrivateKey<N>,
41 ) -> Result<(Record<N, Plaintext<N>>, Record<N, Plaintext<N>>)> {
42 let records = self.find_record_amounts(vec![amount, fee], private_key)?;
43 if records.len() < 2 { bail!("Insufficient funds") } else { Ok((records[0].clone(), records[1].clone())) }
44 }
45
46 pub fn find_one_record(
49 &self,
50 private_key: &PrivateKey<N>,
51 amount: u64,
52 found_nonces: Option<&[Group<N>]>,
53 ) -> Result<Record<N, Plaintext<N>>> {
54 let step_size = 49u32;
55 let amounts = Some(vec![amount]);
56 let current_height = self.api_client.latest_height()?;
57 let mut end_height = current_height;
58 let mut start_height = end_height.saturating_sub(49);
59 for _ in (0..current_height).step_by(step_size as usize) {
60 let result = self
61 .api_client
62 .get_unspent_records(private_key, start_height..end_height, None, amounts.as_ref())
63 .map_or(vec![], |records| records)
64 .into_iter()
65 .find(|(_, record)| {
66 record.microcredits().unwrap_or(0) >= amount && {
67 if let Some(found_nonces) = found_nonces {
68 !found_nonces.contains(record.nonce())
69 } else {
70 true
71 }
72 }
73 })
74 .ok_or_else(|| anyhow!("Insufficient funds"));
75
76 if let Ok(record) = result {
77 return Ok(record.1);
78 }
79 end_height = start_height;
80 start_height = start_height.saturating_sub(step_size);
81 }
82 bail!("Insufficient funds")
83 }
84
85 pub fn find_record_amounts(
90 &self,
91 amounts: Vec<u64>,
92 private_key: &PrivateKey<N>,
93 ) -> Result<Vec<Record<N, Plaintext<N>>>> {
94 self.find_unspent_records_on_chain(Some(&amounts), None, private_key)
95 }
96
97 pub fn find_unspent_records_on_chain(
98 &self,
99 amounts: Option<&Vec<u64>>,
100 max_microcredits: Option<u64>,
101 private_key: &PrivateKey<N>,
102 ) -> Result<Vec<Record<N, Plaintext<N>>>> {
103 let latest_height = self.api_client.latest_height()?;
104 let records = self.api_client.get_unspent_records(private_key, 0..latest_height, max_microcredits, amounts)?;
105 Ok(records.into_iter().map(|(_, record)| record).collect())
106 }
107
108 pub fn find_matching_records_from_program(
110 &self,
111 private_key: &PrivateKey<N>,
112 program_id: &ProgramID<N>,
113 matching_function: impl FnOnce(Vec<Record<N, Plaintext<N>>>) -> Result<Vec<Record<N, Plaintext<N>>>>,
114 unspent_only: bool,
115 max_records: Option<usize>,
116 ) -> Result<Vec<Record<N, Plaintext<N>>>> {
117 let latest_height = self.api_client.latest_height()?;
118 let view_key = ViewKey::try_from(private_key)?;
119 let records = self.api_client.get_program_records(
120 private_key,
121 program_id,
122 0..latest_height,
123 unspent_only,
124 max_records,
125 )?;
126 let decrypted_records =
127 records.into_iter().map(|(_, record)| record.decrypt(&view_key).unwrap()).collect::<Vec<_>>();
128 matching_function(decrypted_records)
129 }
130}