use super::*;
use std::borrow::Cow;
impl<N: Network, B: BlockStorage<N>, P: ProgramStorage<N>> Ledger<N, B, P> {
pub fn find_record_ciphertexts<'a>(
&'a self,
view_key: &'a ViewKey<N>,
filter: RecordsFilter<N>,
) -> Result<impl '_ + Iterator<Item = (Field<N>, Cow<'_, Record<N, Ciphertext<N>>>)>> {
let address = view_key.to_address();
let sk_tag = match GraphKey::try_from(view_key) {
Ok(graph_key) => graph_key.sk_tag(),
Err(e) => bail!("Failed to derive the graph key from the view key: {e}"),
};
fn compute_tag<N: Network>(sk_tag: Field<N>, commitment: Field<N>) -> Result<Field<N>> {
N::hash_psd2(&[sk_tag, commitment])
}
fn compute_serial_number<N: Network>(private_key: PrivateKey<N>, commitment: Field<N>) -> Result<Field<N>> {
let h = N::hash_to_group_psd2(&[N::serial_number_domain(), commitment])?;
let gamma = h * private_key.sk_sig();
let sn_nonce =
N::hash_to_scalar_psd2(&[N::serial_number_domain(), gamma.mul_by_cofactor().to_x_coordinate()])?;
N::commit_bhp512(&(N::serial_number_domain(), commitment).to_bits_le(), &sn_nonce)
}
Ok(self.records().flat_map(move |cow| {
let (commitment, record) = match cow {
(Cow::Borrowed(commitment), record) => (*commitment, record),
(Cow::Owned(commitment), record) => (commitment, record),
};
let commitment = match filter {
RecordsFilter::All => Ok(Some(commitment)),
RecordsFilter::Spent => compute_tag(sk_tag, commitment).and_then(|tag| {
self.contains_tag(&tag).map(|is_spent| match is_spent {
true => Some(commitment),
false => None,
})
}),
RecordsFilter::Unspent => compute_tag(sk_tag, commitment).and_then(|tag| {
self.contains_tag(&tag).map(|is_spent| match is_spent {
true => None,
false => Some(commitment),
})
}),
RecordsFilter::SlowSpent(private_key) => {
compute_serial_number(private_key, commitment).and_then(|serial_number| {
self.contains_serial_number(&serial_number).map(|is_spent| match is_spent {
true => Some(commitment),
false => None,
})
})
}
RecordsFilter::SlowUnspent(private_key) => {
compute_serial_number(private_key, commitment).and_then(|serial_number| {
self.contains_serial_number(&serial_number).map(|is_spent| match is_spent {
true => None,
false => Some(commitment),
})
})
}
};
match commitment {
Ok(Some(commitment)) => match record.is_owner(&address, view_key) {
true => Some((commitment, record)),
false => None,
},
Ok(None) => None,
Err(e) => {
warn!("Failed to process 'find_record_ciphertexts({:?})': {e}", filter);
None
}
}
}))
}
pub fn find_records<'a>(
&'a self,
view_key: &'a ViewKey<N>,
filter: RecordsFilter<N>,
) -> Result<impl '_ + Iterator<Item = (Field<N>, Record<N, Plaintext<N>>)>> {
self.find_record_ciphertexts(view_key, filter).map(|iter| {
iter.flat_map(|(commitment, record)| match record.decrypt(view_key) {
Ok(record) => Some((commitment, record)),
Err(e) => {
warn!("Failed to decrypt the record: {e}");
None
}
})
})
}
}