read: rct outputs

This commit is contained in:
hinto.janai 2024-04-28 15:55:18 -04:00
parent 98157c9347
commit 5b227dbe4f
No known key found for this signature in database
GPG key ID: D47CE05FA175A499
2 changed files with 84 additions and 20 deletions

View file

@ -5,16 +5,17 @@
use cuprate_helper::map::u64_to_timelock; use cuprate_helper::map::u64_to_timelock;
use cuprate_types::OutputOnChain; use cuprate_types::OutputOnChain;
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::CompressedEdwardsY, Scalar}; use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::CompressedEdwardsY, Scalar};
use monero_serai::{transaction::Timelock, H}; use monero_serai::{transaction::Timelock, Commitment, H};
use crate::{ use crate::{
tables::{Tables, TxUnlockTime}, tables::{Tables, TxUnlockTime},
types::{Amount, Output, OutputFlags}, types::{Amount, Output, OutputFlags, RctOutput},
DatabaseRo, RuntimeError, DatabaseRo, RuntimeError,
}; };
//---------------------------------------------------------------------------------------------------- Free functions //---------------------------------------------------------------------------------------------------- Free functions
/// Map a [`crate::types::Output`] to a [`cuprate_types::OutputOnChain`]. /// Map an [`Output`] to a [`cuprate_types::OutputOnChain`].
#[inline]
pub(crate) fn output_to_output_on_chain( pub(crate) fn output_to_output_on_chain(
output: &Output, output: &Output,
amount: Amount, amount: Amount,
@ -45,6 +46,43 @@ pub(crate) fn output_to_output_on_chain(
}) })
} }
/// Map an [`RctOutput`] to a [`cuprate_types::OutputOnChain`].
///
/// # Panics
/// This function will panic if `rct_output`'s `commitment` fails to decompress into a valid [`EdwardsPoint`].
#[inline]
pub(crate) fn rct_output_to_output_on_chain(
rct_output: &RctOutput,
amount: Amount,
table_tx_unlock_time: &impl DatabaseRo<TxUnlockTime>,
) -> Result<OutputOnChain, RuntimeError> {
// INVARIANT: Commitments stored are valid when stored by the database.
let commitment = CompressedEdwardsY::from_slice(&rct_output.commitment)
.unwrap()
.decompress()
.unwrap();
let time_lock = if rct_output
.output_flags
.contains(OutputFlags::NON_ZERO_UNLOCK_TIME)
{
u64_to_timelock(table_tx_unlock_time.get(&rct_output.tx_idx)?)
} else {
Timelock::None
};
let key = CompressedEdwardsY::from_slice(&rct_output.key)
.map(|y| y.decompress())
.unwrap_or(None);
Ok(OutputOnChain {
height: u64::from(rct_output.height),
time_lock,
key,
commitment,
})
}
//---------------------------------------------------------------------------------------------------- Tests //---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)] #[cfg(test)]
mod test { mod test {

View file

@ -29,12 +29,15 @@ use crate::{
config::ReaderThreads, config::ReaderThreads,
constants::DATABASE_CORRUPT_MSG, constants::DATABASE_CORRUPT_MSG,
error::RuntimeError, error::RuntimeError,
free::output_to_output_on_chain, free::{
output_to_output_on_chain, output_v1_or_v2_to_output_on_chain,
rct_output_to_output_on_chain,
},
ops::{ ops::{
block::{get_block_extended_header_from_height, get_block_info}, block::{get_block_extended_header_from_height, get_block_info},
blockchain::{cumulative_generated_coins, top_block_height}, blockchain::{cumulative_generated_coins, top_block_height},
key_image::key_image_exists, key_image::key_image_exists,
output::get_output, output::{get_output, get_rct_output},
}, },
service::types::{ResponseReceiver, ResponseResult, ResponseSender}, service::types::{ResponseReceiver, ResponseResult, ResponseSender},
tables::{BlockHeights, BlockInfos, KeyImages, NumOutputs, Outputs, Tables}, tables::{BlockHeights, BlockInfos, KeyImages, NumOutputs, Outputs, Tables},
@ -431,15 +434,26 @@ fn outputs(env: &ConcreteEnv, map: HashMap<Amount, HashSet<AmountIndex>>) -> Res
let inner_map = |amount, amount_index| { let inner_map = |amount, amount_index| {
let (tx_ro, tables) = get_tx_ro_and_tables!(env_inner, tx_ro, tables); let (tx_ro, tables) = get_tx_ro_and_tables!(env_inner, tx_ro, tables);
if amount == 0 {
// v2 transactions.
let rct_output = get_rct_output(&amount_index, tables.rct_outputs())?;
let output_on_chain =
rct_output_to_output_on_chain(&rct_output, amount, tables.tx_unlock_time())?;
Ok((amount_index, output_on_chain))
} else {
// v1 transactions.
let pre_rct_output_id = PreRctOutputId { let pre_rct_output_id = PreRctOutputId {
amount, amount,
amount_index, amount_index,
}; };
let output = get_output(&pre_rct_output_id, tables.outputs())?; let output = get_output(&pre_rct_output_id, tables.outputs())?;
let output_on_chain = output_to_output_on_chain(&output, amount, tables.tx_unlock_time())?; let output_on_chain =
output_to_output_on_chain(&output, amount, tables.tx_unlock_time())?;
Ok((amount_index, output_on_chain)) Ok((amount_index, output_on_chain))
}
}; };
let map = map let map = map
@ -465,11 +479,22 @@ fn number_outputs_with_amount(env: &ConcreteEnv, amounts: Vec<Amount>) -> Respon
let tx_ro = set_tx_ro!(env, env_inner); let tx_ro = set_tx_ro!(env, env_inner);
let tables = set_tables!(env, env_inner, tx_ro); let tables = set_tables!(env, env_inner, tx_ro);
// Cache the amount of RCT outputs once.
let (_, tables) = get_tx_ro_and_tables!(env_inner, tx_ro, tables);
// INVARIANT: #[cfg] @ lib.rs asserts `usize == u64`
#[allow(clippy::cast_possible_truncation)]
let num_rct_outputs = tables.rct_outputs().len()? as usize;
let map = amounts let map = amounts
.into_par_iter() .into_par_iter()
.map(|amount| { .map(|amount| {
let (tx_ro, tables) = get_tx_ro_and_tables!(env_inner, tx_ro, tables); let (tx_ro, tables) = get_tx_ro_and_tables!(env_inner, tx_ro, tables);
if amount == 0 {
// v2 transactions.
Ok((amount, num_rct_outputs))
} else {
// v1 transactions.
match tables.num_outputs().get(&amount) { match tables.num_outputs().get(&amount) {
// INVARIANT: #[cfg] @ lib.rs asserts `usize == u64` // INVARIANT: #[cfg] @ lib.rs asserts `usize == u64`
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
@ -479,6 +504,7 @@ fn number_outputs_with_amount(env: &ConcreteEnv, amounts: Vec<Amount>) -> Respon
Err(RuntimeError::KeyNotFound) => Ok((amount, 0)), Err(RuntimeError::KeyNotFound) => Ok((amount, 0)),
Err(e) => Err(e), Err(e) => Err(e),
} }
}
}) })
.collect::<Result<HashMap<Amount, usize>, RuntimeError>>()?; .collect::<Result<HashMap<Amount, usize>, RuntimeError>>()?;