mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-23 12:09:52 +00:00
read: rct outputs
This commit is contained in:
parent
98157c9347
commit
5b227dbe4f
2 changed files with 84 additions and 20 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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>>()?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue