mirror of
https://github.com/hinto-janai/cuprate.git
synced 2025-01-21 10:14:30 +00:00
This commit is contained in:
parent
4118af2f24
commit
0b3ff4e14c
7 changed files with 136 additions and 36 deletions
|
@ -16,6 +16,7 @@ use cuprate_rpc_types::{
|
||||||
misc::BlockHeader,
|
misc::BlockHeader,
|
||||||
};
|
};
|
||||||
use cuprate_types::HardFork;
|
use cuprate_types::HardFork;
|
||||||
|
use monero_serai::transaction::Timelock;
|
||||||
|
|
||||||
use crate::rpc::{
|
use crate::rpc::{
|
||||||
service::{blockchain, blockchain_context},
|
service::{blockchain, blockchain_context},
|
||||||
|
@ -170,6 +171,23 @@ pub(super) async fn top_height(state: &mut CupratedRpcHandler) -> Result<(u64, [
|
||||||
Ok((height, hash))
|
Ok((height, hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the `timelock` is unlocked.
|
||||||
|
pub(super) async fn timelock_is_unlocked(
|
||||||
|
state: &mut CupratedRpcHandler,
|
||||||
|
timelock: Timelock,
|
||||||
|
) -> Result<bool, Error> {
|
||||||
|
let unlocked = match timelock {
|
||||||
|
Timelock::None => true,
|
||||||
|
Timelock::Block(height) => {
|
||||||
|
let (top_height, _) = top_height(state).await?;
|
||||||
|
top_height > usize_to_u64(height)
|
||||||
|
}
|
||||||
|
Timelock::Time(timestamp) => cuprate_helper::time::current_unix_timestamp() > timestamp,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(unlocked)
|
||||||
|
}
|
||||||
|
|
||||||
/// TODO: impl bootstrap
|
/// TODO: impl bootstrap
|
||||||
pub const fn response_base(is_bootstrap: bool) -> ResponseBase {
|
pub const fn response_base(is_bootstrap: bool) -> ResponseBase {
|
||||||
if is_bootstrap {
|
if is_bootstrap {
|
||||||
|
|
|
@ -467,8 +467,10 @@ async fn get_info(
|
||||||
blockchain::alt_chain_count(&mut state.blockchain_read).await?
|
blockchain::alt_chain_count(&mut state.blockchain_read).await?
|
||||||
};
|
};
|
||||||
|
|
||||||
let block_weight_limit = usize_to_u64(c.effective_median_weight);
|
// TODO: these values are incorrectly calculated in `monerod` and copied in `cuprated`
|
||||||
let block_weight_median = usize_to_u64(c.median_weight_for_block_reward);
|
// <https://github.com/Cuprate/cuprate/pull/355#discussion_r1906273007>
|
||||||
|
let block_weight_limit = usize_to_u64(c.effective_median_weight * 2);
|
||||||
|
let block_weight_median = usize_to_u64(c.effective_median_weight);
|
||||||
let block_size_limit = block_weight_limit;
|
let block_size_limit = block_weight_limit;
|
||||||
let block_size_median = block_weight_median;
|
let block_size_median = block_weight_median;
|
||||||
|
|
||||||
|
@ -969,9 +971,11 @@ async fn get_miner_data(
|
||||||
let major_version = c.current_hf.as_u8();
|
let major_version = c.current_hf.as_u8();
|
||||||
let height = usize_to_u64(c.chain_height);
|
let height = usize_to_u64(c.chain_height);
|
||||||
let prev_id = Hex(c.top_hash);
|
let prev_id = Hex(c.top_hash);
|
||||||
let seed_hash = Hex(c.top_hash);
|
// TODO: RX seed hash <https://github.com/Cuprate/cuprate/pull/355#discussion_r1911814320>.
|
||||||
|
let seed_hash = Hex([0; 32]);
|
||||||
let difficulty = c.next_difficulty.hex_prefix();
|
let difficulty = c.next_difficulty.hex_prefix();
|
||||||
let median_weight = usize_to_u64(c.median_weight_for_block_reward);
|
// TODO: <https://github.com/Cuprate/cuprate/pull/355#discussion_r1911821515>
|
||||||
|
let median_weight = usize_to_u64(c.effective_median_weight);
|
||||||
let already_generated_coins = c.already_generated_coins;
|
let already_generated_coins = c.already_generated_coins;
|
||||||
let tx_backlog = txpool::backlog(&mut state.txpool_read)
|
let tx_backlog = txpool::backlog(&mut state.txpool_read)
|
||||||
.await?
|
.await?
|
||||||
|
@ -1061,7 +1065,7 @@ async fn add_aux_pow(
|
||||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::task::spawn_blocking(|| add_aux_pow_inner(state, request)).await?
|
cuprate_helper::asynch::rayon_spawn_async(|| add_aux_pow_inner(state, request)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2072-L2207>
|
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2072-L2207>
|
||||||
|
|
|
@ -45,34 +45,22 @@ pub(super) async fn get_outs(
|
||||||
return Err(anyhow!("Too many outs requested"));
|
return Err(anyhow!("Too many outs requested"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let outputs = {
|
let outputs = blockchain::outputs_vec(&mut state.blockchain_read, request.outputs).await?;
|
||||||
let mut outputs = HashMap::<u64, HashSet<u64>>::with_capacity(request.outputs.len());
|
let mut outs = Vec::<OutKeyBin>::with_capacity(outputs.len());
|
||||||
|
|
||||||
for out in request.outputs {
|
for (_, index_vec) in outputs {
|
||||||
outputs
|
for (_, out) in index_vec {
|
||||||
.entry(out.amount)
|
let out_key = OutKeyBin {
|
||||||
.and_modify(|set| {
|
|
||||||
set.insert(out.index);
|
|
||||||
})
|
|
||||||
.or_insert_with(|| HashSet::from([out.index]));
|
|
||||||
}
|
|
||||||
|
|
||||||
outputs
|
|
||||||
};
|
|
||||||
|
|
||||||
let outs = blockchain::outputs(&mut state.blockchain_read, outputs)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|(amount, index_map)| {
|
|
||||||
index_map.into_values().map(|out| OutKeyBin {
|
|
||||||
key: out.key.map_or([0; 32], |e| e.compress().0),
|
key: out.key.map_or([0; 32], |e| e.compress().0),
|
||||||
mask: out.commitment.compress().0,
|
mask: out.commitment.compress().0,
|
||||||
unlocked: matches!(out.time_lock, Timelock::None),
|
unlocked: helper::timelock_is_unlocked(&mut state, out.time_lock).await?,
|
||||||
height: usize_to_u64(out.height),
|
height: usize_to_u64(out.height),
|
||||||
txid: if request.get_txid { out.txid } else { [0; 32] },
|
txid: if request.get_txid { out.txid } else { [0; 32] },
|
||||||
})
|
};
|
||||||
})
|
|
||||||
.collect::<Vec<OutKeyBin>>();
|
outs.push(out_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(GetOutsResponse {
|
Ok(GetOutsResponse {
|
||||||
base: helper::access_response_base(false),
|
base: helper::access_response_base(false),
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
|
use cuprate_rpc_types::misc::GetOutputsOut;
|
||||||
use monero_serai::block::Block;
|
use monero_serai::block::Block;
|
||||||
use tower::{Service, ServiceExt};
|
use tower::{Service, ServiceExt};
|
||||||
|
|
||||||
|
@ -203,6 +204,28 @@ pub async fn outputs(
|
||||||
Ok(outputs)
|
Ok(outputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`BlockchainReadRequest::OutputsVec`]
|
||||||
|
pub async fn outputs_vec(
|
||||||
|
blockchain_read: &mut BlockchainReadHandle,
|
||||||
|
outputs: Vec<GetOutputsOut>,
|
||||||
|
) -> Result<Vec<(u64, Vec<(u64, OutputOnChain)>)>, Error> {
|
||||||
|
let outputs = outputs
|
||||||
|
.into_iter()
|
||||||
|
.map(|output| (output.amount, output.index))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let BlockchainResponse::OutputsVec(outputs) = blockchain_read
|
||||||
|
.ready()
|
||||||
|
.await?
|
||||||
|
.call(BlockchainReadRequest::OutputsVec(outputs))
|
||||||
|
.await?
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(outputs)
|
||||||
|
}
|
||||||
|
|
||||||
/// [`BlockchainReadRequest::NumberOutputsWithAmount`]
|
/// [`BlockchainReadRequest::NumberOutputsWithAmount`]
|
||||||
pub async fn number_outputs_with_amount(
|
pub async fn number_outputs_with_amount(
|
||||||
blockchain_read: &mut BlockchainReadHandle,
|
blockchain_read: &mut BlockchainReadHandle,
|
||||||
|
|
|
@ -119,8 +119,10 @@ fn map_request(
|
||||||
R::ChainHeight => chain_height(env),
|
R::ChainHeight => chain_height(env),
|
||||||
R::GeneratedCoins(height) => generated_coins(env, height),
|
R::GeneratedCoins(height) => generated_coins(env, height),
|
||||||
R::Outputs(map) => outputs(env, map),
|
R::Outputs(map) => outputs(env, map),
|
||||||
|
R::OutputsVec(vec) => outputs_vec(env, vec),
|
||||||
R::NumberOutputsWithAmount(vec) => number_outputs_with_amount(env, vec),
|
R::NumberOutputsWithAmount(vec) => number_outputs_with_amount(env, vec),
|
||||||
R::KeyImagesSpent(set) => key_images_spent(env, set),
|
R::KeyImagesSpent(set) => key_images_spent(env, set),
|
||||||
|
R::KeyImagesSpentVec(set) => key_images_spent_vec(env, set),
|
||||||
R::CompactChainHistory => compact_chain_history(env),
|
R::CompactChainHistory => compact_chain_history(env),
|
||||||
R::NextChainEntry(block_hashes, amount) => next_chain_entry(env, &block_hashes, amount),
|
R::NextChainEntry(block_hashes, amount) => next_chain_entry(env, &block_hashes, amount),
|
||||||
R::FindFirstUnknown(block_ids) => find_first_unknown(env, &block_ids),
|
R::FindFirstUnknown(block_ids) => find_first_unknown(env, &block_ids),
|
||||||
|
@ -487,6 +489,12 @@ fn outputs(env: &ConcreteEnv, outputs: HashMap<Amount, HashSet<AmountIndex>>) ->
|
||||||
Ok(BlockchainResponse::Outputs(map))
|
Ok(BlockchainResponse::Outputs(map))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`BlockchainReadRequest::OutputsVec`].
|
||||||
|
#[inline]
|
||||||
|
fn outputs_vec(env: &ConcreteEnv, outputs: Vec<(Amount, AmountIndex)>) -> ResponseResult {
|
||||||
|
Ok(BlockchainResponse::OutputsVec(todo!()))
|
||||||
|
}
|
||||||
|
|
||||||
/// [`BlockchainReadRequest::NumberOutputsWithAmount`].
|
/// [`BlockchainReadRequest::NumberOutputsWithAmount`].
|
||||||
#[inline]
|
#[inline]
|
||||||
fn number_outputs_with_amount(env: &ConcreteEnv, amounts: Vec<Amount>) -> ResponseResult {
|
fn number_outputs_with_amount(env: &ConcreteEnv, amounts: Vec<Amount>) -> ResponseResult {
|
||||||
|
@ -538,7 +546,43 @@ fn number_outputs_with_amount(env: &ConcreteEnv, amounts: Vec<Amount>) -> Respon
|
||||||
|
|
||||||
/// [`BlockchainReadRequest::KeyImagesSpent`].
|
/// [`BlockchainReadRequest::KeyImagesSpent`].
|
||||||
#[inline]
|
#[inline]
|
||||||
fn key_images_spent(env: &ConcreteEnv, key_images: Vec<KeyImage>) -> ResponseResult {
|
fn key_images_spent(env: &ConcreteEnv, key_images: HashSet<KeyImage>) -> ResponseResult {
|
||||||
|
// Prepare tx/tables in `ThreadLocal`.
|
||||||
|
let env_inner = env.env_inner();
|
||||||
|
let tx_ro = thread_local(env);
|
||||||
|
let tables = thread_local(env);
|
||||||
|
|
||||||
|
// Key image check function.
|
||||||
|
let key_image_exists = |key_image| {
|
||||||
|
let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?;
|
||||||
|
let tables = get_tables!(env_inner, tx_ro, tables)?.as_ref();
|
||||||
|
key_image_exists(&key_image, tables.key_images())
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
// Create/use `enum cuprate_types::Exist { Does, DoesNot }`
|
||||||
|
// or similar instead of `bool` for clarity.
|
||||||
|
// <https://github.com/Cuprate/cuprate/pull/113#discussion_r1581536526>
|
||||||
|
//
|
||||||
|
// Collect results using `rayon`.
|
||||||
|
match key_images
|
||||||
|
.into_par_iter()
|
||||||
|
.map(key_image_exists)
|
||||||
|
// If the result is either:
|
||||||
|
// `Ok(true)` => a key image was found, return early
|
||||||
|
// `Err` => an error was found, return early
|
||||||
|
//
|
||||||
|
// Else, `Ok(false)` will continue the iterator.
|
||||||
|
.find_any(|result| !matches!(result, Ok(false)))
|
||||||
|
{
|
||||||
|
None | Some(Ok(false)) => Ok(BlockchainResponse::KeyImagesSpent(false)), // Key image was NOT found.
|
||||||
|
Some(Ok(true)) => Ok(BlockchainResponse::KeyImagesSpent(true)), // Key image was found.
|
||||||
|
Some(Err(e)) => Err(e), // A database error occurred.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`BlockchainReadRequest::KeyImagesSpentVec`].
|
||||||
|
fn key_images_spent_vec(env: &ConcreteEnv, key_images: Vec<KeyImage>) -> ResponseResult {
|
||||||
// Prepare tx/tables in `ThreadLocal`.
|
// Prepare tx/tables in `ThreadLocal`.
|
||||||
let env_inner = env.env_inner();
|
let env_inner = env.env_inner();
|
||||||
let tx_ro = thread_local(env);
|
let tx_ro = thread_local(env);
|
||||||
|
@ -552,7 +596,7 @@ fn key_images_spent(env: &ConcreteEnv, key_images: Vec<KeyImage>) -> ResponseRes
|
||||||
};
|
};
|
||||||
|
|
||||||
// Collect results using `rayon`.
|
// Collect results using `rayon`.
|
||||||
Ok(BlockchainResponse::KeyImagesSpent(
|
Ok(BlockchainResponse::KeyImagesSpentVec(
|
||||||
key_images
|
key_images
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(key_image_exists)
|
.map(key_image_exists)
|
||||||
|
|
|
@ -180,7 +180,7 @@ async fn test_template(
|
||||||
|
|
||||||
// Contains a fake non-spent key-image.
|
// Contains a fake non-spent key-image.
|
||||||
let ki_req = vec![[0; 32]];
|
let ki_req = vec![[0; 32]];
|
||||||
let ki_resp = Ok(BlockchainResponse::KeyImagesSpent(vec![false]));
|
let ki_resp = Ok(BlockchainResponse::KeyImagesSpentVec(vec![false]));
|
||||||
|
|
||||||
//----------------------------------------------------------------------- Assert expected response
|
//----------------------------------------------------------------------- Assert expected response
|
||||||
// Assert read requests lead to the expected responses.
|
// Assert read requests lead to the expected responses.
|
||||||
|
@ -218,7 +218,7 @@ async fn test_template(
|
||||||
BlockchainReadRequest::NumberOutputsWithAmount(num_req),
|
BlockchainReadRequest::NumberOutputsWithAmount(num_req),
|
||||||
num_resp,
|
num_resp,
|
||||||
),
|
),
|
||||||
(BlockchainReadRequest::KeyImagesSpent(ki_req), ki_resp),
|
(BlockchainReadRequest::KeyImagesSpentVec(ki_req), ki_resp),
|
||||||
] {
|
] {
|
||||||
let response = reader.clone().oneshot(request).await;
|
let response = reader.clone().oneshot(request).await;
|
||||||
println!("response: {response:#?}, expected_response: {expected_response:#?}");
|
println!("response: {response:#?}, expected_response: {expected_response:#?}");
|
||||||
|
@ -232,12 +232,12 @@ async fn test_template(
|
||||||
// Assert each key image we inserted comes back as "spent".
|
// Assert each key image we inserted comes back as "spent".
|
||||||
for key_image in tables.key_images_iter().keys().unwrap() {
|
for key_image in tables.key_images_iter().keys().unwrap() {
|
||||||
let key_image = key_image.unwrap();
|
let key_image = key_image.unwrap();
|
||||||
let request = BlockchainReadRequest::KeyImagesSpent(vec![key_image]);
|
let request = BlockchainReadRequest::KeyImagesSpentVec(vec![key_image]);
|
||||||
let response = reader.clone().oneshot(request).await;
|
let response = reader.clone().oneshot(request).await;
|
||||||
println!("response: {response:#?}, key_image: {key_image:#?}");
|
println!("response: {response:#?}, key_image: {key_image:#?}");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
response.unwrap(),
|
response.unwrap(),
|
||||||
BlockchainResponse::KeyImagesSpent(vec![true])
|
BlockchainResponse::KeyImagesSpentVec(vec![true])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,13 @@ pub enum BlockchainReadRequest {
|
||||||
/// RCT output indices.
|
/// RCT output indices.
|
||||||
Outputs(HashMap<u64, HashSet<u64>>),
|
Outputs(HashMap<u64, HashSet<u64>>),
|
||||||
|
|
||||||
|
/// This is the same as [`BlockchainReadRequest::Outputs`] but with a [`Vec`] container.
|
||||||
|
///
|
||||||
|
/// The input [`Vec`] values are `(amount, amount_index)`.
|
||||||
|
///
|
||||||
|
/// The response will be in the same order as the request.
|
||||||
|
OutputsVec(Vec<(u64, u64)>),
|
||||||
|
|
||||||
/// Request the amount of outputs with a certain amount.
|
/// Request the amount of outputs with a certain amount.
|
||||||
///
|
///
|
||||||
/// The input is a list of output amounts.
|
/// The input is a list of output amounts.
|
||||||
|
@ -104,7 +111,12 @@ pub enum BlockchainReadRequest {
|
||||||
/// Check the spend status of key images.
|
/// Check the spend status of key images.
|
||||||
///
|
///
|
||||||
/// Input is a set of key images.
|
/// Input is a set of key images.
|
||||||
KeyImagesSpent(Vec<[u8; 32]>),
|
KeyImagesSpent(HashSet<[u8; 32]>),
|
||||||
|
|
||||||
|
/// Same as [`BlockchainReadRequest::KeyImagesSpent`] but with a [`Vec`].
|
||||||
|
///
|
||||||
|
/// The response will be in the same order as the request.
|
||||||
|
KeyImagesSpentVec(Vec<[u8; 32]>),
|
||||||
|
|
||||||
/// A request for the compact chain history.
|
/// A request for the compact chain history.
|
||||||
CompactChainHistory,
|
CompactChainHistory,
|
||||||
|
@ -284,6 +296,9 @@ pub enum BlockchainResponse {
|
||||||
/// associated with their amount and amount index.
|
/// associated with their amount and amount index.
|
||||||
Outputs(HashMap<u64, HashMap<u64, OutputOnChain>>),
|
Outputs(HashMap<u64, HashMap<u64, OutputOnChain>>),
|
||||||
|
|
||||||
|
/// Response to [`BlockchainReadRequest::OutputsVec`].
|
||||||
|
OutputsVec(Vec<(u64, Vec<(u64, OutputOnChain)>)>),
|
||||||
|
|
||||||
/// Response to [`BlockchainReadRequest::NumberOutputsWithAmount`].
|
/// Response to [`BlockchainReadRequest::NumberOutputsWithAmount`].
|
||||||
///
|
///
|
||||||
/// Inner value is a `HashMap` of all the outputs requested where:
|
/// Inner value is a `HashMap` of all the outputs requested where:
|
||||||
|
@ -293,11 +308,19 @@ pub enum BlockchainResponse {
|
||||||
|
|
||||||
/// Response to [`BlockchainReadRequest::KeyImagesSpent`].
|
/// Response to [`BlockchainReadRequest::KeyImagesSpent`].
|
||||||
///
|
///
|
||||||
|
/// The inner value is `true` if _any_ of the key images
|
||||||
|
/// were spent (existed in the database already).
|
||||||
|
///
|
||||||
|
/// The inner value is `false` if _none_ of the key images were spent.
|
||||||
|
KeyImagesSpent(bool),
|
||||||
|
|
||||||
|
/// Response to [`BlockchainReadRequest::KeyImagesSpentVec`].
|
||||||
|
///
|
||||||
/// Inner value is a `Vec` the same length as the input.
|
/// Inner value is a `Vec` the same length as the input.
|
||||||
///
|
///
|
||||||
/// The index of each entry corresponds with the request.
|
/// The index of each entry corresponds with the request.
|
||||||
/// `true` means that the key image was spent.
|
/// `true` means that the key image was spent.
|
||||||
KeyImagesSpent(Vec<bool>),
|
KeyImagesSpentVec(Vec<bool>),
|
||||||
|
|
||||||
/// Response to [`BlockchainReadRequest::CompactChainHistory`].
|
/// Response to [`BlockchainReadRequest::CompactChainHistory`].
|
||||||
CompactChainHistory {
|
CompactChainHistory {
|
||||||
|
|
Loading…
Reference in a new issue