Compare commits

...

4 commits

Author SHA1 Message Date
hinto.janai
f7f9063890
clippy 2024-12-16 20:49:20 -05:00
hinto.janai
3664e2f794
/get_output_distribution.bin 2024-12-16 20:48:06 -05:00
hinto.janai
8cd319cc45
/get_o_indexes.bin 2024-12-16 20:30:52 -05:00
hinto.janai
541302ea07
get_blocks_by_height, shared::get_outs 2024-12-16 20:21:00 -05:00
16 changed files with 320 additions and 151 deletions

View file

@ -9,5 +9,6 @@ mod helper;
mod json; mod json;
mod other; mod other;
mod request; mod request;
mod shared;
pub use handler::CupratedRpcHandler; pub use handler::CupratedRpcHandler;

View file

@ -1,8 +1,10 @@
//! RPC request handler functions (binary endpoints). //! RPC request handler functions (binary endpoints).
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use bytes::Bytes;
use cuprate_constants::rpc::{RESTRICTED_BLOCK_COUNT, RESTRICTED_TRANSACTIONS_COUNT}; use cuprate_constants::rpc::{RESTRICTED_BLOCK_COUNT, RESTRICTED_TRANSACTIONS_COUNT};
use cuprate_fixed_bytes::ByteArrayVec;
use cuprate_rpc_interface::RpcHandler; use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{ use cuprate_rpc_types::{
base::{AccessResponseBase, ResponseBase}, base::{AccessResponseBase, ResponseBase},
@ -17,7 +19,11 @@ use cuprate_rpc_types::{
}; };
use cuprate_types::{rpc::PoolInfoExtent, BlockCompleteEntry}; use cuprate_types::{rpc::PoolInfoExtent, BlockCompleteEntry};
use crate::rpc::{helper, request::blockchain, CupratedRpcHandler}; use crate::rpc::{
helper,
request::{blockchain, txpool},
shared, CupratedRpcHandler,
};
/// Map a [`BinRequest`] to the function that will lead to a [`BinResponse`]. /// Map a [`BinRequest`] to the function that will lead to a [`BinResponse`].
pub(super) async fn map_request( pub(super) async fn map_request(
@ -122,18 +128,16 @@ async fn get_blocks(
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L817-L857> /// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L817-L857>
async fn get_blocks_by_height( async fn get_blocks_by_height(
state: CupratedRpcHandler, mut state: CupratedRpcHandler,
request: GetBlocksByHeightRequest, request: GetBlocksByHeightRequest,
) -> Result<GetBlocksByHeightResponse, Error> { ) -> Result<GetBlocksByHeightResponse, Error> {
if state.is_restricted() && request.heights.len() > RESTRICTED_BLOCK_COUNT { if state.is_restricted() && request.heights.len() > RESTRICTED_BLOCK_COUNT {
return Err(anyhow!("Too many blocks requested in restricted mode")); return Err(anyhow!("Too many blocks requested in restricted mode"));
} }
let blocks = request let blocks =
.heights blockchain::block_complete_entries_by_height(&mut state.blockchain_read, request.heights)
.into_iter() .await?;
.map(|height| Ok(todo!()))
.collect::<Result<Vec<BlockCompleteEntry>, Error>>()?;
Ok(GetBlocksByHeightResponse { Ok(GetBlocksByHeightResponse {
base: helper::access_response_base(false), base: helper::access_response_base(false),
@ -157,13 +161,6 @@ async fn get_hashes(
request.block_ids[len - 1] request.block_ids[len - 1]
}; };
const GENESIS_BLOCK_HASH: [u8; 32] = [0; 32]; // TODO
if last != GENESIS_BLOCK_HASH {
return Err(anyhow!(
"genesis block mismatch, found: {last:?}, expected: {GENESIS_BLOCK_HASH:?}"
));
}
let mut bytes = request.block_ids; let mut bytes = request.block_ids;
let hashes: Vec<[u8; 32]> = (&bytes).into(); let hashes: Vec<[u8; 32]> = (&bytes).into();
@ -187,12 +184,12 @@ async fn get_hashes(
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L959-L977> /// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L959-L977>
async fn get_output_indexes( async fn get_output_indexes(
state: CupratedRpcHandler, mut state: CupratedRpcHandler,
request: GetOutputIndexesRequest, request: GetOutputIndexesRequest,
) -> Result<GetOutputIndexesResponse, Error> { ) -> Result<GetOutputIndexesResponse, Error> {
Ok(GetOutputIndexesResponse { Ok(GetOutputIndexesResponse {
base: helper::access_response_base(false), base: helper::access_response_base(false),
..todo!() o_indexes: blockchain::tx_output_indexes(&mut state.blockchain_read, request.txid).await?,
}) })
} }
@ -201,20 +198,19 @@ async fn get_outs(
state: CupratedRpcHandler, state: CupratedRpcHandler,
request: GetOutsRequest, request: GetOutsRequest,
) -> Result<GetOutsResponse, Error> { ) -> Result<GetOutsResponse, Error> {
Ok(GetOutsResponse { shared::get_outs(state, request).await
base: helper::access_response_base(false),
..todo!()
})
} }
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1689-L1711> /// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1689-L1711>
async fn get_transaction_pool_hashes( async fn get_transaction_pool_hashes(
state: CupratedRpcHandler, mut state: CupratedRpcHandler,
request: GetTransactionPoolHashesRequest, _: GetTransactionPoolHashesRequest,
) -> Result<GetTransactionPoolHashesResponse, Error> { ) -> Result<GetTransactionPoolHashesResponse, Error> {
Ok(GetTransactionPoolHashesResponse { Ok(GetTransactionPoolHashesResponse {
base: helper::access_response_base(false), base: helper::access_response_base(false),
..todo!() tx_hashes: shared::get_transaction_pool_hashes(state)
.await
.map(ByteArrayVec::from)?,
}) })
} }
@ -223,8 +219,5 @@ async fn get_output_distribution(
state: CupratedRpcHandler, state: CupratedRpcHandler,
request: GetOutputDistributionRequest, request: GetOutputDistributionRequest,
) -> Result<GetOutputDistributionResponse, Error> { ) -> Result<GetOutputDistributionResponse, Error> {
Ok(GetOutputDistributionResponse { shared::get_output_distribution(state, request).await
base: helper::access_response_base(false),
..todo!()
})
} }

View file

@ -62,7 +62,7 @@ use crate::{
rpc::{ rpc::{
helper, helper,
request::{address_book, blockchain, blockchain_context, blockchain_manager, txpool}, request::{address_book, blockchain, blockchain_context, blockchain_manager, txpool},
CupratedRpcHandler, shared, CupratedRpcHandler,
}, },
statics::START_INSTANT_UNIX, statics::START_INSTANT_UNIX,
}; };
@ -958,35 +958,10 @@ async fn get_transaction_pool_backlog(
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3352-L3398> /// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3352-L3398>
async fn get_output_distribution( async fn get_output_distribution(
mut state: CupratedRpcHandler, state: CupratedRpcHandler,
request: GetOutputDistributionRequest, request: GetOutputDistributionRequest,
) -> Result<GetOutputDistributionResponse, Error> { ) -> Result<GetOutputDistributionResponse, Error> {
if state.is_restricted() && request.amounts != [1, 0] { shared::get_output_distribution(state, request).await
return Err(anyhow!(
"Restricted RPC can only get output distribution for RCT outputs. Use your own node."
));
}
// 0 is placeholder for the whole chain
let req_to_height = if request.to_height == 0 {
helper::top_height(&mut state).await?.0.saturating_sub(1)
} else {
request.to_height
};
let distributions = request.amounts.into_iter().map(|amount| {
fn get_output_distribution() -> Result<Distribution, Error> {
todo!("https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a/src/rpc/rpc_handler.cpp#L29");
Err(anyhow!("Failed to get output distribution"))
}
get_output_distribution()
}).collect::<Result<Vec<Distribution>, _>>()?;
Ok(GetOutputDistributionResponse {
base: helper::access_response_base(false),
distributions,
})
} }
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1998-L2033> /// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1998-L2033>

View file

@ -37,7 +37,7 @@ use cuprate_rpc_types::{
}, },
}; };
use cuprate_types::{ use cuprate_types::{
rpc::{KeyImageSpentStatus, OutKey, PoolInfo, PoolTxInfo, PublicNode}, rpc::{KeyImageSpentStatus, PoolInfo, PoolTxInfo, PublicNode},
TxInPool, TxRelayChecks, TxInPool, TxRelayChecks,
}; };
use monero_serai::transaction::{Input, Timelock, Transaction}; use monero_serai::transaction::{Input, Timelock, Transaction};
@ -47,7 +47,7 @@ use crate::{
rpc::{ rpc::{
helper, helper,
request::{blockchain, blockchain_context, blockchain_manager, txpool}, request::{blockchain, blockchain_context, blockchain_manager, txpool},
CupratedRpcHandler, shared, CupratedRpcHandler,
}, },
statics::START_INSTANT_UNIX, statics::START_INSTANT_UNIX,
}; };
@ -610,45 +610,21 @@ async fn get_net_stats(
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L912-L957> /// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L912-L957>
async fn get_outs( async fn get_outs(
mut state: CupratedRpcHandler, state: CupratedRpcHandler,
request: GetOutsRequest, request: GetOutsRequest,
) -> Result<GetOutsResponse, Error> { ) -> Result<GetOutsResponse, Error> {
if state.is_restricted() && request.outputs.len() > MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT { let outs = shared::get_outs(
return Err(anyhow!("Too many outs requested")); state,
} cuprate_rpc_types::bin::GetOutsRequest {
outputs: request.outputs,
let outputs = { get_txid: request.get_txid,
let mut outputs = HashMap::<u64, HashSet<u64>>::with_capacity(request.outputs.len()); },
)
for out in request.outputs { .await?
outputs .outs
.entry(out.amount) .into_iter()
.and_modify(|set| { .map(Into::into)
set.insert(out.index); .collect();
})
.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_iter().map(|(index, out)| OutKey {
key: out.key.map_or(Hex([0; 32]), |e| Hex(e.compress().0)),
mask: Hex(out.commitment.compress().0),
unlocked: matches!(out.time_lock, Timelock::None),
height: usize_to_u64(out.height),
txid: if request.get_txid {
hex::encode(out.txid)
} else {
String::new()
},
})
})
.collect::<Vec<OutKey>>();
Ok(GetOutsResponse { Ok(GetOutsResponse {
base: helper::response_base(false), base: helper::response_base(false),
@ -675,20 +651,13 @@ async fn get_transaction_pool_hashes(
mut state: CupratedRpcHandler, mut state: CupratedRpcHandler,
_: GetTransactionPoolHashesRequest, _: GetTransactionPoolHashesRequest,
) -> Result<GetTransactionPoolHashesResponse, Error> { ) -> Result<GetTransactionPoolHashesResponse, Error> {
let include_sensitive_txs = !state.is_restricted();
// FIXME: this request is a bit overkill, we only need the hashes.
// We could create a separate request for this.
let tx_hashes = txpool::pool(&mut state.txpool_read, include_sensitive_txs)
.await?
.0
.into_iter()
.map(|tx| tx.id_hash)
.collect();
Ok(GetTransactionPoolHashesResponse { Ok(GetTransactionPoolHashesResponse {
base: helper::response_base(false), base: helper::response_base(false),
tx_hashes, tx_hashes: shared::get_transaction_pool_hashes(state)
.await?
.into_iter()
.map(Hex)
.collect(),
}) })
} }

View file

@ -16,7 +16,7 @@ use cuprate_types::{
rpc::{ rpc::{
ChainInfo, CoinbaseTxSum, KeyImageSpentStatus, OutputHistogramEntry, OutputHistogramInput, ChainInfo, CoinbaseTxSum, KeyImageSpentStatus, OutputHistogramEntry, OutputHistogramInput,
}, },
Chain, ExtendedBlockHeader, OutputOnChain, TxInBlockchain, BlockCompleteEntry, Chain, ExtendedBlockHeader, OutputOnChain, TxInBlockchain,
}; };
/// [`BlockchainReadRequest::Block`]. /// [`BlockchainReadRequest::Block`].
@ -399,7 +399,7 @@ pub(crate) async fn transactions(
pub(crate) async fn total_rct_outputs( pub(crate) async fn total_rct_outputs(
blockchain_read: &mut BlockchainReadHandle, blockchain_read: &mut BlockchainReadHandle,
) -> Result<u64, Error> { ) -> Result<u64, Error> {
let BlockchainResponse::TotalRctOutputs(total_rct_outputs) = blockchain_read let BlockchainResponse::TotalRctOutputs(n) = blockchain_read
.ready() .ready()
.await? .await?
.call(BlockchainReadRequest::TotalRctOutputs) .call(BlockchainReadRequest::TotalRctOutputs)
@ -408,5 +408,41 @@ pub(crate) async fn total_rct_outputs(
unreachable!(); unreachable!();
}; };
Ok(total_rct_outputs) Ok(n)
}
/// [`BlockchainReadRequest::BlockCompleteEntriesByHeight`].
pub(crate) async fn block_complete_entries_by_height(
blockchain_read: &mut BlockchainReadHandle,
block_heights: Vec<u64>,
) -> Result<Vec<BlockCompleteEntry>, Error> {
let BlockchainResponse::BlockCompleteEntriesByHeight(blocks) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::BlockCompleteEntriesByHeight(
block_heights.into_iter().map(u64_to_usize).collect(),
))
.await?
else {
unreachable!();
};
Ok(blocks)
}
/// [`BlockchainReadRequest::TxOutputIndexes`].
pub(crate) async fn tx_output_indexes(
blockchain_read: &mut BlockchainReadHandle,
tx_hash: [u8; 32],
) -> Result<Vec<u64>, Error> {
let BlockchainResponse::TxOutputIndexes(o_indexes) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::TxOutputIndexes { tx_hash })
.await?
else {
unreachable!();
};
Ok(o_indexes)
} }

View file

@ -0,0 +1,132 @@
//! RPC handler functions that are shared between different endpoint/methods.
use std::collections::{HashMap, HashSet};
use anyhow::{anyhow, Error};
use monero_serai::transaction::Timelock;
use cuprate_constants::rpc::MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT;
use cuprate_helper::cast::usize_to_u64;
use cuprate_hex::Hex;
use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{
bin::{
GetOutsRequest, GetOutsResponse, GetTransactionPoolHashesRequest,
GetTransactionPoolHashesResponse,
},
json::{GetOutputDistributionRequest, GetOutputDistributionResponse},
misc::{Distribution, OutKeyBin},
};
use crate::rpc::{
helper,
request::{blockchain, txpool},
CupratedRpcHandler,
};
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L912-L957>
///
/// Shared between:
/// - Other JSON's `/get_outs`
/// - Binary's `/get_outs.bin`
pub(super) async fn get_outs(
mut state: CupratedRpcHandler,
request: GetOutsRequest,
) -> Result<GetOutsResponse, Error> {
if state.is_restricted() && request.outputs.len() > MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT {
return Err(anyhow!("Too many outs requested"));
}
let outputs = {
let mut outputs = HashMap::<u64, HashSet<u64>>::with_capacity(request.outputs.len());
for out in request.outputs {
outputs
.entry(out.amount)
.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),
mask: out.commitment.compress().0,
unlocked: matches!(out.time_lock, Timelock::None),
height: usize_to_u64(out.height),
txid: if request.get_txid { out.txid } else { [0; 32] },
})
})
.collect::<Vec<OutKeyBin>>();
Ok(GetOutsResponse {
base: helper::access_response_base(false),
outs,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1713-L1739>
///
/// Shared between:
/// - Other JSON's `/get_transaction_pool_hashes`
/// - Binary's `/get_transaction_pool_hashes.bin`
///
/// Returns transaction hashes.
pub(super) async fn get_transaction_pool_hashes(
mut state: CupratedRpcHandler,
) -> Result<Vec<[u8; 32]>, Error> {
let include_sensitive_txs = !state.is_restricted();
// FIXME: this request is a bit overkill, we only need the hashes.
// We could create a separate request for this.
Ok(txpool::pool(&mut state.txpool_read, include_sensitive_txs)
.await?
.0
.into_iter()
.map(|tx| tx.id_hash.0)
.collect())
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3352-L3398>
///
/// Shared between:
/// - JSON-RPC's `get_output_distribution`
/// - Binary's `/get_output_distribution.bin`
pub(super) async fn get_output_distribution(
mut state: CupratedRpcHandler,
request: GetOutputDistributionRequest,
) -> Result<GetOutputDistributionResponse, Error> {
if state.is_restricted() && request.amounts != [1, 0] {
return Err(anyhow!(
"Restricted RPC can only get output distribution for RCT outputs. Use your own node."
));
}
// 0 is placeholder for the whole chain
let req_to_height = if request.to_height == 0 {
helper::top_height(&mut state).await?.0.saturating_sub(1)
} else {
request.to_height
};
let distributions = request.amounts.into_iter().map(|amount| {
fn get_output_distribution() -> Result<Distribution, Error> {
todo!("https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a/src/rpc/rpc_handler.cpp#L29");
Err(anyhow!("Failed to get output distribution"))
}
get_output_distribution()
}).collect::<Result<Vec<Distribution>, _>>()?;
Ok(GetOutputDistributionResponse {
base: helper::access_response_base(false),
distributions,
})
}

View file

@ -120,7 +120,6 @@ define_request_and_response! {
Request { Request {
requested_info: u8 = default::<u8>(), "default", requested_info: u8 = default::<u8>(), "default",
// FIXME: This is a `std::list` in `monerod` because...?
block_ids: ByteArrayVec<32> = default::<ByteArrayVec<32>>(), "default", block_ids: ByteArrayVec<32> = default::<ByteArrayVec<32>>(), "default",
start_height: u64, start_height: u64,
prune: bool, prune: bool,

View file

@ -193,3 +193,15 @@ impl From<SpentKeyImageInfo> for crate::misc::SpentKeyImageInfo {
} }
} }
} }
impl From<crate::misc::OutKeyBin> for crate::misc::OutKey {
fn from(x: crate::misc::OutKeyBin) -> Self {
Self {
key: Hex(x.key),
mask: Hex(x.mask),
unlocked: x.unlocked,
height: x.height,
txid: hex::encode(x.txid),
}
}
}

View file

@ -25,6 +25,6 @@ pub use requested_info::RequestedInfo;
pub use status::Status; pub use status::Status;
pub use tx_entry::{TxEntry, TxEntryType}; pub use tx_entry::{TxEntry, TxEntryType};
pub use types::{ pub use types::{
BlockHeader, ChainInfo, ConnectionInfo, GetBan, GetOutputsOut, HistogramEntry, OutKeyBin, BlockHeader, ChainInfo, ConnectionInfo, GetBan, GetOutputsOut, HistogramEntry, OutKey,
SetBan, Span, SpentKeyImageInfo, SyncInfoPeer, TxInfo, OutKeyBin, SetBan, Span, SpentKeyImageInfo, SyncInfoPeer, TxInfo,
}; };

View file

@ -252,6 +252,23 @@ define_struct_and_impl_epee! {
} }
} }
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
582..=597
)]
/// Used in [`crate::other::GetOutsRequest`].
OutKey {
key: Hex<32>,
mask: Hex<32>,
unlocked: bool,
height: u64,
/// Optionally empty with `/get_outs`'s `"get_txid": false`.
txid: String,
}
}
define_struct_and_impl_epee! { define_struct_and_impl_epee! {
#[doc = monero_definition_link!( #[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454", "cc73fe71162d564ffda8e549b79a350bca53c454",

View file

@ -7,12 +7,12 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use cuprate_hex::Hex; use cuprate_hex::Hex;
use cuprate_types::rpc::{OutKey, Peer, PublicNode, TxpoolStats}; use cuprate_types::rpc::{Peer, PublicNode, TxpoolStats};
use crate::{ use crate::{
base::{AccessResponseBase, ResponseBase}, base::{AccessResponseBase, ResponseBase},
macros::define_request_and_response, macros::define_request_and_response,
misc::{GetOutputsOut, SpentKeyImageInfo, Status, TxEntry, TxInfo}, misc::{GetOutputsOut, OutKey, SpentKeyImageInfo, Status, TxEntry, TxInfo},
RpcCallValue, RpcCallValue,
}; };
@ -1206,9 +1206,8 @@ mod test {
mask: Hex(hex!( mask: Hex(hex!(
"1738eb7a677c6149228a2beaa21bea9e3370802d72a3eec790119580e02bd522" "1738eb7a677c6149228a2beaa21bea9e3370802d72a3eec790119580e02bd522"
)), )),
txid: Hex(hex!( txid: "9d651903b80fb70b9935b72081cd967f543662149aed3839222511acd9100601"
"9d651903b80fb70b9935b72081cd967f543662149aed3839222511acd9100601" .into(),
)),
unlocked: true, unlocked: true,
}, },
OutKey { OutKey {
@ -1219,9 +1218,8 @@ mod test {
mask: Hex(hex!( mask: Hex(hex!(
"1738eb7a677c6149228a2beaa21bea9e3370802d72a3eec790119580e02bd522" "1738eb7a677c6149228a2beaa21bea9e3370802d72a3eec790119580e02bd522"
)), )),
txid: Hex(hex!( txid: "230bff732dc5f225df14fff82aadd1bf11b3fb7ad3a03413c396a617e843f7d0"
"230bff732dc5f225df14fff82aadd1bf11b3fb7ad3a03413c396a617e843f7d0" .into(),
)),
unlocked: true, unlocked: true,
}, },
], ],

View file

@ -254,7 +254,7 @@ pub fn get_block_blob_with_tx_indexes(
Ok((block, miner_tx_idx, numb_txs)) Ok((block, miner_tx_idx, numb_txs))
} }
//---------------------------------------------------------------------------------------------------- `get_block_extended_header_*` //---------------------------------------------------------------------------------------------------- `get_block_complete_entry_*`
/// Retrieve a [`BlockCompleteEntry`] from the database. /// Retrieve a [`BlockCompleteEntry`] from the database.
/// ///
#[doc = doc_error!()] #[doc = doc_error!()]
@ -263,8 +263,18 @@ pub fn get_block_complete_entry(
tables: &impl TablesIter, tables: &impl TablesIter,
) -> Result<BlockCompleteEntry, RuntimeError> { ) -> Result<BlockCompleteEntry, RuntimeError> {
let block_height = tables.block_heights().get(block_hash)?; let block_height = tables.block_heights().get(block_hash)?;
get_block_complete_entry_from_height(&block_height, tables)
}
/// Retrieve a [`BlockCompleteEntry`] from the database.
///
#[doc = doc_error!()]
pub fn get_block_complete_entry_from_height(
block_height: &BlockHeight,
tables: &impl TablesIter,
) -> Result<BlockCompleteEntry, RuntimeError> {
let (block_blob, miner_tx_idx, numb_non_miner_txs) = let (block_blob, miner_tx_idx, numb_non_miner_txs) =
get_block_blob_with_tx_indexes(&block_height, tables)?; get_block_blob_with_tx_indexes(block_height, tables)?;
let first_tx_idx = miner_tx_idx + 1; let first_tx_idx = miner_tx_idx + 1;

View file

@ -41,7 +41,8 @@ use crate::{
}, },
block::{ block::{
block_exists, get_block_blob_with_tx_indexes, get_block_complete_entry, block_exists, get_block_blob_with_tx_indexes, get_block_complete_entry,
get_block_extended_header_from_height, get_block_height, get_block_info, get_block_complete_entry_from_height, get_block_extended_header_from_height,
get_block_height, get_block_info,
}, },
blockchain::{cumulative_generated_coins, find_split_point, top_block_height}, blockchain::{cumulative_generated_coins, find_split_point, top_block_height},
key_image::key_image_exists, key_image::key_image_exists,
@ -53,6 +54,7 @@ use crate::{
}, },
tables::{ tables::{
AltBlockHeights, BlockHeights, BlockInfos, OpenTables, RctOutputs, Tables, TablesIter, AltBlockHeights, BlockHeights, BlockInfos, OpenTables, RctOutputs, Tables, TablesIter,
TxIds, TxOutputs,
}, },
types::{ types::{
AltBlockHeight, Amount, AmountIndex, BlockHash, BlockHeight, KeyImage, PreRctOutputId, AltBlockHeight, Amount, AmountIndex, BlockHash, BlockHeight, KeyImage, PreRctOutputId,
@ -108,6 +110,7 @@ fn map_request(
match request { match request {
R::BlockCompleteEntries(block_hashes) => block_complete_entries(env, block_hashes), R::BlockCompleteEntries(block_hashes) => block_complete_entries(env, block_hashes),
R::BlockCompleteEntriesByHeight(heights) => block_complete_entries_by_height(env, heights),
R::BlockExtendedHeader(block) => block_extended_header(env, block), R::BlockExtendedHeader(block) => block_extended_header(env, block),
R::BlockHash(block, chain) => block_hash(env, block, chain), R::BlockHash(block, chain) => block_hash(env, block, chain),
R::FindBlock(block_hash) => find_block(env, block_hash), R::FindBlock(block_hash) => find_block(env, block_hash),
@ -138,6 +141,7 @@ fn map_request(
R::AltChainCount => alt_chain_count(env), R::AltChainCount => alt_chain_count(env),
R::Transactions { tx_hashes } => transactions(env, tx_hashes), R::Transactions { tx_hashes } => transactions(env, tx_hashes),
R::TotalRctOutputs => total_rct_outputs(env), R::TotalRctOutputs => total_rct_outputs(env),
R::TxOutputIndexes { tx_hash } => tx_output_indexes(env, &tx_hash),
} }
/* SOMEDAY: post-request handling, run some code for each request? */ /* SOMEDAY: post-request handling, run some code for each request? */
@ -245,6 +249,31 @@ fn block_complete_entries(env: &ConcreteEnv, block_hashes: Vec<BlockHash>) -> Re
}) })
} }
/// [`BlockchainReadRequest::BlockCompleteEntriesByHeight`].
fn block_complete_entries_by_height(
env: &ConcreteEnv,
block_heights: Vec<BlockHeight>,
) -> ResponseResult {
// Prepare tx/tables in `ThreadLocal`.
let env_inner = env.env_inner();
let tx_ro = thread_local(env);
let tables = thread_local(env);
let blocks = block_heights
.into_par_iter()
.map(|height| {
let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?;
let tables = get_tables!(env_inner, tx_ro, tables)?.as_ref();
get_block_complete_entry_from_height(&height, tables)
})
.collect::<DbResult<_>>()?;
let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?;
let tables = get_tables!(env_inner, tx_ro, tables)?.as_ref();
Ok(BlockchainResponse::BlockCompleteEntriesByHeight(blocks))
}
/// [`BlockchainReadRequest::BlockExtendedHeader`]. /// [`BlockchainReadRequest::BlockExtendedHeader`].
#[inline] #[inline]
fn block_extended_header(env: &ConcreteEnv, block_height: BlockHeight) -> ResponseResult { fn block_extended_header(env: &ConcreteEnv, block_height: BlockHeight) -> ResponseResult {
@ -792,3 +821,14 @@ fn total_rct_outputs(env: &ConcreteEnv) -> ResponseResult {
Ok(BlockchainResponse::TotalRctOutputs(len)) Ok(BlockchainResponse::TotalRctOutputs(len))
} }
/// [`BlockchainReadRequest::TxOutputIndexes`]
fn tx_output_indexes(env: &ConcreteEnv, tx_hash: &[u8; 32]) -> ResponseResult {
// Single-threaded, no `ThreadLocal` required.
let env_inner = env.env_inner();
let tx_ro = env_inner.tx_ro()?;
let tx_id = env_inner.open_db_ro::<TxIds>(&tx_ro)?.get(tx_hash)?;
let o_indexes = env_inner.open_db_ro::<TxOutputs>(&tx_ro)?.get(&tx_id)?;
Ok(BlockchainResponse::TxOutputIndexes(o_indexes.0))
}

View file

@ -32,6 +32,11 @@ pub enum BlockchainReadRequest {
/// The input is the block hashes. /// The input is the block hashes.
BlockCompleteEntries(Vec<[u8; 32]>), BlockCompleteEntries(Vec<[u8; 32]>),
/// Request [`BlockCompleteEntry`]s.
///
/// The input is the block heights.
BlockCompleteEntriesByHeight(Vec<usize>),
/// Request a block's extended header. /// Request a block's extended header.
/// ///
/// The input is the block's height. /// The input is the block's height.
@ -166,6 +171,9 @@ pub enum BlockchainReadRequest {
/// TODO /// TODO
TotalRctOutputs, TotalRctOutputs,
/// TODO
TxOutputIndexes { tx_hash: [u8; 32] },
} }
//---------------------------------------------------------------------------------------------------- WriteRequest //---------------------------------------------------------------------------------------------------- WriteRequest
@ -223,6 +231,9 @@ pub enum BlockchainResponse {
blockchain_height: usize, blockchain_height: usize,
}, },
/// Response to [`BlockchainReadRequest::BlockCompleteEntriesByHeight`].
BlockCompleteEntriesByHeight(Vec<BlockCompleteEntry>),
/// Response to [`BlockchainReadRequest::BlockExtendedHeader`]. /// Response to [`BlockchainReadRequest::BlockExtendedHeader`].
/// ///
/// Inner value is the extended headed of the requested block. /// Inner value is the extended headed of the requested block.
@ -364,6 +375,9 @@ pub enum BlockchainResponse {
/// Response to [`BlockchainReadRequest::TotalRctOutputs`]. /// Response to [`BlockchainReadRequest::TotalRctOutputs`].
TotalRctOutputs(u64), TotalRctOutputs(u64),
/// Response to [`BlockchainReadRequest::TxOutputIndexes`].
TxOutputIndexes(Vec<u64>),
//------------------------------------------------------ Writes //------------------------------------------------------ Writes
/// A generic Ok response to indicate a request was successfully handled. /// A generic Ok response to indicate a request was successfully handled.
/// ///

View file

@ -16,7 +16,7 @@ pub use pool_info_extent::PoolInfoExtent;
pub use types::{ pub use types::{
AddAuxPow, AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, CoinbaseTxSum, ConnectionInfo, AddAuxPow, AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, CoinbaseTxSum, ConnectionInfo,
FeeEstimate, GetBan, GetMinerDataTxBacklogEntry, GetOutputsOut, HardForkEntry, HardForkInfo, FeeEstimate, GetBan, GetMinerDataTxBacklogEntry, GetOutputsOut, HardForkEntry, HardForkInfo,
HistogramEntry, MinerData, MinerDataTxBacklogEntry, OutKey, OutKeyBin, OutputDistributionData, HistogramEntry, MinerData, MinerDataTxBacklogEntry, OutputDistributionData,
OutputHistogramEntry, OutputHistogramInput, Peer, PoolInfoFull, PoolInfoIncremental, OutputHistogramEntry, OutputHistogramInput, Peer, PoolInfoFull, PoolInfoIncremental,
PoolTxInfo, PublicNode, SetBan, Span, SpentKeyImageInfo, SyncInfoPeer, TxBacklogEntry, TxInfo, PoolTxInfo, PublicNode, SetBan, Span, SpentKeyImageInfo, SyncInfoPeer, TxBacklogEntry, TxInfo,
TxOutputIndices, TxpoolHisto, TxpoolStats, TxOutputIndices, TxpoolHisto, TxpoolStats,

View file

@ -308,19 +308,6 @@ define_struct_and_impl_epee! {
index: u64, index: u64,
} }
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
538..=553
)]
OutKeyBin {
key: [u8; 32],
mask: [u8; 32],
unlocked: bool,
height: u64,
txid: [u8; 32],
}
#[doc = monero_definition_link!( #[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454", "cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h", "rpc/core_rpc_server_commands_defs.h",
@ -419,20 +406,6 @@ define_struct_and_impl_epee! {
txs_total: u32, txs_total: u32,
} }
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
582..=597
)]
OutKey {
key: Hex<32>,
mask: Hex<32>,
unlocked: bool,
height: u64,
// Optionally empty with `/get_outs`'s `"get_txid": false`.
txid: String,
}
#[doc = monero_definition_link!( #[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a", "893916ad091a92e765ce3241b94e706ad012b62a",
"blockchain_db/lmdb/db_lmdb.cpp", "blockchain_db/lmdb/db_lmdb.cpp",