mirror of
https://github.com/hinto-janai/cuprate.git
synced 2025-01-20 17:54:31 +00:00
Compare commits
2 commits
b402c82557
...
a359eed8be
Author | SHA1 | Date | |
---|---|---|---|
|
a359eed8be | ||
|
c3bc9b26ec |
18 changed files with 526 additions and 256 deletions
|
@ -11,7 +11,7 @@ use cuprate_helper::{
|
||||||
cast::{u64_to_usize, usize_to_u64},
|
cast::{u64_to_usize, usize_to_u64},
|
||||||
map::split_u128_into_low_high_bits,
|
map::split_u128_into_low_high_bits,
|
||||||
};
|
};
|
||||||
use cuprate_rpc_types::misc::{BlockHeader, KeyImageSpentStatus};
|
use cuprate_rpc_types::misc::BlockHeader;
|
||||||
use cuprate_types::HardFork;
|
use cuprate_types::HardFork;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -166,18 +166,3 @@ pub(super) async fn top_height(state: &mut CupratedRpcHandler) -> Result<(u64, [
|
||||||
let height = chain_height.saturating_sub(1);
|
let height = chain_height.saturating_sub(1);
|
||||||
Ok((height, hash))
|
Ok((height, hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a key image is spent.
|
|
||||||
pub(super) async fn key_image_spent(
|
|
||||||
state: &mut CupratedRpcHandler,
|
|
||||||
key_image: [u8; 32],
|
|
||||||
) -> Result<KeyImageSpentStatus, Error> {
|
|
||||||
todo!("impl key image vec check responding KeyImageSpentStatus")
|
|
||||||
// if blockchain::key_image_spent(state, key_image).await? {
|
|
||||||
// Ok(KeyImageSpentStatus::SpentInBlockchain)
|
|
||||||
// } else if todo!("key image is spent in tx pool") {
|
|
||||||
// Ok(KeyImageSpentStatus::SpentInPool)
|
|
||||||
// } else {
|
|
||||||
// Ok(KeyImageSpentStatus::Unspent)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
//! RPC request handler functions (other JSON endpoints).
|
//! RPC request handler functions (other JSON endpoints).
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
|
|
||||||
use cuprate_constants::rpc::{
|
use cuprate_constants::rpc::{
|
||||||
MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT, RESTRICTED_SPENT_KEY_IMAGES_COUNT,
|
MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT, RESTRICTED_SPENT_KEY_IMAGES_COUNT,
|
||||||
|
RESTRICTED_TRANSACTIONS_COUNT,
|
||||||
};
|
};
|
||||||
use cuprate_helper::cast::usize_to_u64;
|
use cuprate_helper::cast::usize_to_u64;
|
||||||
use cuprate_hex::Hex;
|
use cuprate_hex::Hex;
|
||||||
use cuprate_rpc_interface::RpcHandler;
|
use cuprate_rpc_interface::RpcHandler;
|
||||||
use cuprate_rpc_types::{
|
use cuprate_rpc_types::{
|
||||||
base::{AccessResponseBase, ResponseBase},
|
base::{AccessResponseBase, ResponseBase},
|
||||||
misc::{KeyImageSpentStatus, Status},
|
misc::{Status, TxEntry, TxEntryType},
|
||||||
other::{
|
other::{
|
||||||
GetAltBlocksHashesRequest, GetAltBlocksHashesResponse, GetHeightRequest, GetHeightResponse,
|
GetAltBlocksHashesRequest, GetAltBlocksHashesResponse, GetHeightRequest, GetHeightResponse,
|
||||||
GetLimitRequest, GetLimitResponse, GetNetStatsRequest, GetNetStatsResponse, GetOutsRequest,
|
GetLimitRequest, GetLimitResponse, GetNetStatsRequest, GetNetStatsResponse, GetOutsRequest,
|
||||||
|
@ -31,15 +32,20 @@ use cuprate_rpc_types::{
|
||||||
UpdateRequest, UpdateResponse,
|
UpdateRequest, UpdateResponse,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use cuprate_types::rpc::OutKey;
|
use cuprate_types::{
|
||||||
|
rpc::{KeyImageSpentStatus, OutKey, PoolInfo, PoolTxInfo},
|
||||||
|
TxInPool,
|
||||||
|
};
|
||||||
|
use monero_serai::transaction::Transaction;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
rpc::CupratedRpcHandler,
|
rpc::CupratedRpcHandler,
|
||||||
rpc::{helper, request::blockchain},
|
rpc::{
|
||||||
|
helper,
|
||||||
|
request::{blockchain, blockchain_context, blockchain_manager, txpool},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::request::blockchain_manager;
|
|
||||||
|
|
||||||
/// Map a [`OtherRequest`] to the function that will lead to a [`OtherResponse`].
|
/// Map a [`OtherRequest`] to the function that will lead to a [`OtherResponse`].
|
||||||
pub(super) async fn map_request(
|
pub(super) async fn map_request(
|
||||||
state: CupratedRpcHandler,
|
state: CupratedRpcHandler,
|
||||||
|
@ -112,23 +118,148 @@ async fn get_height(
|
||||||
|
|
||||||
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L979-L1227>
|
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L979-L1227>
|
||||||
async fn get_transactions(
|
async fn get_transactions(
|
||||||
state: CupratedRpcHandler,
|
mut state: CupratedRpcHandler,
|
||||||
request: GetTransactionsRequest,
|
request: GetTransactionsRequest,
|
||||||
) -> Result<GetTransactionsResponse, Error> {
|
) -> Result<GetTransactionsResponse, Error> {
|
||||||
|
if state.is_restricted() && request.txs_hashes.len() > RESTRICTED_TRANSACTIONS_COUNT {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Too many transactions requested in restricted mode"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (txs_in_blockchain, missed_txs) = {
|
||||||
|
let requested_txs = request.txs_hashes.into_iter().map(|tx| tx.0).collect();
|
||||||
|
blockchain::transactions(&mut state.blockchain_read, requested_txs).await?
|
||||||
|
};
|
||||||
|
|
||||||
|
let missed_tx = missed_txs.clone().into_iter().map(Hex).collect();
|
||||||
|
|
||||||
|
// Check the txpool for missed transactions.
|
||||||
|
let txs_in_pool = if missed_txs.is_empty() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
let include_sensitive_txs = !state.is_restricted();
|
||||||
|
txpool::txs_by_hash(&mut state.txpool_read, missed_txs, include_sensitive_txs).await?
|
||||||
|
};
|
||||||
|
|
||||||
|
let (txs, txs_as_json) = {
|
||||||
|
// Prepare the final JSON output.
|
||||||
|
let len = txs_in_blockchain.len() + txs_in_pool.len();
|
||||||
|
let mut txs = Vec::with_capacity(len);
|
||||||
|
let mut txs_as_json = Vec::with_capacity(if request.decode_as_json { len } else { 0 });
|
||||||
|
|
||||||
|
// Map all blockchain transactions.
|
||||||
|
for tx in txs_in_blockchain {
|
||||||
|
let tx_hash = Hex(tx.tx_hash);
|
||||||
|
let pruned_as_hex = hex::encode(tx.pruned_blob);
|
||||||
|
let prunable_as_hex = hex::encode(tx.prunable_blob);
|
||||||
|
let prunable_hash = Hex(tx.prunable_hash);
|
||||||
|
|
||||||
|
let as_json = if request.decode_as_json {
|
||||||
|
let tx = Transaction::read(&mut tx.tx_blob.as_slice())?;
|
||||||
|
let json_type = cuprate_types::json::tx::Transaction::from(tx);
|
||||||
|
let json = serde_json::to_string(&json_type).unwrap();
|
||||||
|
txs_as_json.push(json.clone());
|
||||||
|
json
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tx_entry_type = TxEntryType::Blockchain {
|
||||||
|
block_height: tx.block_height,
|
||||||
|
block_timestamp: tx.block_timestamp,
|
||||||
|
confirmations: tx.confirmations,
|
||||||
|
output_indices: tx.output_indices,
|
||||||
|
in_pool: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let tx = TxEntry {
|
||||||
|
as_hex: String::new(),
|
||||||
|
as_json,
|
||||||
|
double_spend_seen: false,
|
||||||
|
tx_hash,
|
||||||
|
prunable_as_hex,
|
||||||
|
prunable_hash,
|
||||||
|
pruned_as_hex,
|
||||||
|
tx_entry_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
txs.push(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map all txpool transactions.
|
||||||
|
for tx_in_pool in txs_in_pool {
|
||||||
|
let TxInPool {
|
||||||
|
tx_blob,
|
||||||
|
tx_hash,
|
||||||
|
double_spend_seen,
|
||||||
|
received_timestamp,
|
||||||
|
relayed,
|
||||||
|
} = tx_in_pool;
|
||||||
|
|
||||||
|
let tx_hash = Hex(tx_hash);
|
||||||
|
let tx = Transaction::read(&mut tx_blob.as_slice())?;
|
||||||
|
|
||||||
|
// TODO: pruned data.
|
||||||
|
let pruned_as_hex = String::new();
|
||||||
|
let prunable_as_hex = String::new();
|
||||||
|
let prunable_hash = Hex([0; 32]);
|
||||||
|
|
||||||
|
let as_json = if request.decode_as_json {
|
||||||
|
let json_type = cuprate_types::json::tx::Transaction::from(tx);
|
||||||
|
let json = serde_json::to_string(&json_type).unwrap();
|
||||||
|
txs_as_json.push(json.clone());
|
||||||
|
json
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tx_entry_type = TxEntryType::Pool {
|
||||||
|
relayed,
|
||||||
|
received_timestamp,
|
||||||
|
in_pool: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let tx = TxEntry {
|
||||||
|
as_hex: String::new(),
|
||||||
|
as_json,
|
||||||
|
double_spend_seen,
|
||||||
|
tx_hash,
|
||||||
|
prunable_as_hex,
|
||||||
|
prunable_hash,
|
||||||
|
pruned_as_hex,
|
||||||
|
tx_entry_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
txs.push(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
(txs, txs_as_json)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(GetTransactionsResponse {
|
Ok(GetTransactionsResponse {
|
||||||
base: AccessResponseBase::OK,
|
base: AccessResponseBase::OK,
|
||||||
..todo!()
|
txs_as_hex: vec![],
|
||||||
|
txs_as_json,
|
||||||
|
missed_tx,
|
||||||
|
txs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L790-L815>
|
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L790-L815>
|
||||||
async fn get_alt_blocks_hashes(
|
async fn get_alt_blocks_hashes(
|
||||||
state: CupratedRpcHandler,
|
mut state: CupratedRpcHandler,
|
||||||
request: GetAltBlocksHashesRequest,
|
request: GetAltBlocksHashesRequest,
|
||||||
) -> Result<GetAltBlocksHashesResponse, Error> {
|
) -> Result<GetAltBlocksHashesResponse, Error> {
|
||||||
|
let blks_hashes = blockchain::alt_chains(&mut state.blockchain_read)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|info| Hex(info.block_hash))
|
||||||
|
.collect();
|
||||||
|
|
||||||
Ok(GetAltBlocksHashesResponse {
|
Ok(GetAltBlocksHashesResponse {
|
||||||
base: AccessResponseBase::OK,
|
base: AccessResponseBase::OK,
|
||||||
..todo!()
|
blks_hashes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,16 +268,41 @@ async fn is_key_image_spent(
|
||||||
mut state: CupratedRpcHandler,
|
mut state: CupratedRpcHandler,
|
||||||
request: IsKeyImageSpentRequest,
|
request: IsKeyImageSpentRequest,
|
||||||
) -> Result<IsKeyImageSpentResponse, Error> {
|
) -> Result<IsKeyImageSpentResponse, Error> {
|
||||||
if state.is_restricted() && request.key_images.len() > RESTRICTED_SPENT_KEY_IMAGES_COUNT {
|
let restricted = state.is_restricted();
|
||||||
|
|
||||||
|
if restricted && request.key_images.len() > RESTRICTED_SPENT_KEY_IMAGES_COUNT {
|
||||||
return Err(anyhow!("Too many key images queried in restricted mode"));
|
return Err(anyhow!("Too many key images queried in restricted mode"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut spent_status = Vec::with_capacity(request.key_images.len());
|
let key_images = request
|
||||||
|
.key_images
|
||||||
|
.into_iter()
|
||||||
|
.map(|k| k.0)
|
||||||
|
.collect::<Vec<[u8; 32]>>();
|
||||||
|
|
||||||
for key_image in request.key_images {
|
let mut spent_status = Vec::with_capacity(key_images.len());
|
||||||
let status = helper::key_image_spent(&mut state, key_image.0).await?;
|
|
||||||
spent_status.push(status.to_u8());
|
blockchain::key_images_spent(&mut state.blockchain_read, key_images.clone())
|
||||||
}
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|ki| {
|
||||||
|
if ki {
|
||||||
|
spent_status.push(KeyImageSpentStatus::SpentInBlockchain.to_u8());
|
||||||
|
} else {
|
||||||
|
spent_status.push(KeyImageSpentStatus::Unspent.to_u8());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
txpool::key_images_spent(&mut state.txpool_read, key_images, !restricted)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|ki| {
|
||||||
|
if ki {
|
||||||
|
spent_status.push(KeyImageSpentStatus::SpentInPool.to_u8());
|
||||||
|
} else {
|
||||||
|
spent_status.push(KeyImageSpentStatus::Unspent.to_u8());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(IsKeyImageSpentResponse {
|
Ok(IsKeyImageSpentResponse {
|
||||||
base: AccessResponseBase::OK,
|
base: AccessResponseBase::OK,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Functions to send [`BlockchainReadRequest`]s.
|
//! Functions to send [`BlockchainReadRequest`]s.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{BTreeSet, HashMap, HashSet},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,8 +13,10 @@ use cuprate_blockchain::service::BlockchainReadHandle;
|
||||||
use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
|
use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
|
||||||
use cuprate_types::{
|
use cuprate_types::{
|
||||||
blockchain::{BlockchainReadRequest, BlockchainResponse},
|
blockchain::{BlockchainReadRequest, BlockchainResponse},
|
||||||
rpc::{ChainInfo, CoinbaseTxSum, OutputHistogramEntry, OutputHistogramInput},
|
rpc::{
|
||||||
Chain, ExtendedBlockHeader, OutputOnChain,
|
ChainInfo, CoinbaseTxSum, KeyImageSpentStatus, OutputHistogramEntry, OutputHistogramInput,
|
||||||
|
},
|
||||||
|
Chain, ExtendedBlockHeader, OutputOnChain, TxInBlockchain,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// [`BlockchainReadRequest::Block`].
|
/// [`BlockchainReadRequest::Block`].
|
||||||
|
@ -221,9 +223,9 @@ pub(crate) async fn number_outputs_with_amount(
|
||||||
/// [`BlockchainReadRequest::KeyImagesSpent`]
|
/// [`BlockchainReadRequest::KeyImagesSpent`]
|
||||||
pub(crate) async fn key_images_spent(
|
pub(crate) async fn key_images_spent(
|
||||||
blockchain_read: &mut BlockchainReadHandle,
|
blockchain_read: &mut BlockchainReadHandle,
|
||||||
key_images: HashSet<[u8; 32]>,
|
key_images: Vec<[u8; 32]>,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<Vec<bool>, Error> {
|
||||||
let BlockchainResponse::KeyImagesSpent(is_spent) = blockchain_read
|
let BlockchainResponse::KeyImagesSpent(status) = blockchain_read
|
||||||
.ready()
|
.ready()
|
||||||
.await?
|
.await?
|
||||||
.call(BlockchainReadRequest::KeyImagesSpent(key_images))
|
.call(BlockchainReadRequest::KeyImagesSpent(key_images))
|
||||||
|
@ -232,7 +234,7 @@ pub(crate) async fn key_images_spent(
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(is_spent)
|
Ok(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`BlockchainReadRequest::CompactChainHistory`]
|
/// [`BlockchainReadRequest::CompactChainHistory`]
|
||||||
|
@ -375,3 +377,20 @@ pub(crate) async fn alt_chain_count(
|
||||||
|
|
||||||
Ok(usize_to_u64(count))
|
Ok(usize_to_u64(count))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`BlockchainReadRequest::Transactions`].
|
||||||
|
pub(crate) async fn transactions(
|
||||||
|
blockchain_read: &mut BlockchainReadHandle,
|
||||||
|
tx_hashes: HashSet<[u8; 32]>,
|
||||||
|
) -> Result<(Vec<TxInBlockchain>, Vec<[u8; 32]>), Error> {
|
||||||
|
let BlockchainResponse::Transactions { txs, missed_txs } = blockchain_read
|
||||||
|
.ready()
|
||||||
|
.await?
|
||||||
|
.call(BlockchainReadRequest::Transactions { tx_hashes })
|
||||||
|
.await?
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((txs, missed_txs))
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,10 @@ use cuprate_txpool::{
|
||||||
},
|
},
|
||||||
TxEntry,
|
TxEntry,
|
||||||
};
|
};
|
||||||
use cuprate_types::rpc::{PoolInfo, PoolInfoFull, PoolInfoIncremental, PoolTxInfo};
|
use cuprate_types::{
|
||||||
|
rpc::{PoolInfo, PoolInfoFull, PoolInfoIncremental, PoolTxInfo},
|
||||||
|
TxInPool,
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME: use `anyhow::Error` over `tower::BoxError` in txpool.
|
// FIXME: use `anyhow::Error` over `tower::BoxError` in txpool.
|
||||||
|
|
||||||
|
@ -60,8 +63,8 @@ pub(crate) async fn pool_info(
|
||||||
include_sensitive_txs: bool,
|
include_sensitive_txs: bool,
|
||||||
max_tx_count: usize,
|
max_tx_count: usize,
|
||||||
start_time: Option<NonZero<usize>>,
|
start_time: Option<NonZero<usize>>,
|
||||||
) -> Result<Vec<PoolInfo>, Error> {
|
) -> Result<PoolInfo, Error> {
|
||||||
let TxpoolReadResponse::PoolInfo(vec) = txpool_read
|
let TxpoolReadResponse::PoolInfo(pool_info) = txpool_read
|
||||||
.ready()
|
.ready()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!(e))?
|
.map_err(|e| anyhow!(e))?
|
||||||
|
@ -76,7 +79,53 @@ pub(crate) async fn pool_info(
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(vec)
|
Ok(pool_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub(crate) async fn txs_by_hash(
|
||||||
|
txpool_read: &mut TxpoolReadHandle,
|
||||||
|
tx_hashes: Vec<[u8; 32]>,
|
||||||
|
include_sensitive_txs: bool,
|
||||||
|
) -> Result<Vec<TxInPool>, Error> {
|
||||||
|
let TxpoolReadResponse::TxsByHash(txs_in_pool) = txpool_read
|
||||||
|
.ready()
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?
|
||||||
|
.call(TxpoolReadRequest::TxsByHash {
|
||||||
|
tx_hashes,
|
||||||
|
include_sensitive_txs,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(txs_in_pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub(crate) async fn key_images_spent(
|
||||||
|
txpool_read: &mut TxpoolReadHandle,
|
||||||
|
key_images: Vec<[u8; 32]>,
|
||||||
|
include_sensitive_txs: bool,
|
||||||
|
) -> Result<Vec<bool>, Error> {
|
||||||
|
let TxpoolReadResponse::KeyImagesSpent(status) = txpool_read
|
||||||
|
.ready()
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?
|
||||||
|
.call(TxpoolReadRequest::KeyImagesSpent {
|
||||||
|
key_images,
|
||||||
|
include_sensitive_txs,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO
|
/// TODO
|
||||||
|
|
|
@ -232,16 +232,18 @@ where
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let BlockchainResponse::KeyImagesSpent(kis_spent) = database
|
let BlockchainResponse::KeyImagesSpent(kis_status) = database
|
||||||
.ready()
|
.ready()
|
||||||
.await?
|
.await?
|
||||||
.call(BlockchainReadRequest::KeyImagesSpent(spent_kis))
|
.call(BlockchainReadRequest::KeyImagesSpent(
|
||||||
|
spent_kis.into_iter().collect(),
|
||||||
|
))
|
||||||
.await?
|
.await?
|
||||||
else {
|
else {
|
||||||
panic!("Database sent incorrect response!");
|
panic!("Database sent incorrect response!");
|
||||||
};
|
};
|
||||||
|
|
||||||
if kis_spent {
|
if kis_status.contains(&true) {
|
||||||
tracing::debug!("One or more key images in batch already spent.");
|
tracing::debug!("One or more key images in batch already spent.");
|
||||||
return Err(ConsensusError::Transaction(TransactionError::KeyImageSpent).into());
|
return Err(ConsensusError::Transaction(TransactionError::KeyImageSpent).into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,9 @@ fn dummy_database(outputs: BTreeMap<u64, OutputOnChain>) -> impl Database + Clon
|
||||||
|
|
||||||
BlockchainResponse::Outputs(ret)
|
BlockchainResponse::Outputs(ret)
|
||||||
}
|
}
|
||||||
BlockchainReadRequest::KeyImagesSpent(_) => BlockchainResponse::KeyImagesSpent(false),
|
BlockchainReadRequest::KeyImagesSpent(_) => {
|
||||||
|
BlockchainResponse::KeyImagesSpent(vec![false])
|
||||||
|
}
|
||||||
_ => panic!("Database request not needed for this test"),
|
_ => panic!("Database request not needed for this test"),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
//---------------------------------------------------------------------------------------------------- Mod
|
//---------------------------------------------------------------------------------------------------- Mod
|
||||||
mod binary_string;
|
mod binary_string;
|
||||||
mod distribution;
|
mod distribution;
|
||||||
mod key_image_spent_status;
|
|
||||||
mod requested_info;
|
mod requested_info;
|
||||||
mod status;
|
mod status;
|
||||||
mod tx_entry;
|
mod tx_entry;
|
||||||
|
@ -22,10 +21,9 @@ mod types;
|
||||||
|
|
||||||
pub use binary_string::BinaryString;
|
pub use binary_string::BinaryString;
|
||||||
pub use distribution::{Distribution, DistributionCompressedBinary, DistributionUncompressed};
|
pub use distribution::{Distribution, DistributionCompressedBinary, DistributionUncompressed};
|
||||||
pub use key_image_spent_status::KeyImageSpentStatus;
|
|
||||||
pub use requested_info::RequestedInfo;
|
pub use requested_info::RequestedInfo;
|
||||||
pub use status::Status;
|
pub use status::Status;
|
||||||
pub use tx_entry::TxEntry;
|
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, OutKeyBin,
|
||||||
SetBan, Span, SpentKeyImageInfo, SyncInfoPeer, TxInfo,
|
SetBan, Span, SpentKeyImageInfo, SyncInfoPeer, TxInfo,
|
||||||
|
|
|
@ -11,6 +11,8 @@ use cuprate_epee_encoding::{
|
||||||
EpeeObject, EpeeObjectBuilder,
|
EpeeObject, EpeeObjectBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use cuprate_hex::Hex;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::serde::{serde_false, serde_true};
|
use crate::serde::{serde_false, serde_true};
|
||||||
|
|
||||||
|
@ -28,100 +30,63 @@ use crate::serde::{serde_false, serde_true};
|
||||||
///
|
///
|
||||||
/// It is only implemented to satisfy the RPC type generator
|
/// It is only implemented to satisfy the RPC type generator
|
||||||
/// macro, which requires all objects to be serde + epee.
|
/// macro, which requires all objects to be serde + epee.
|
||||||
///
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
/// # Example
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
/// ```rust
|
pub struct TxEntry {
|
||||||
/// use cuprate_rpc_types::misc::*;
|
/// `cuprate_types::json::tx::Transaction` should be used
|
||||||
/// use serde_json::{json, from_value};
|
/// to create this JSON string in a type-safe manner.
|
||||||
///
|
pub as_json: String,
|
||||||
/// let json = json!({
|
|
||||||
/// "as_hex": String::default(),
|
pub as_hex: String,
|
||||||
/// "as_json": String::default(),
|
pub double_spend_seen: bool,
|
||||||
/// "block_height": u64::default(),
|
pub tx_hash: Hex<32>,
|
||||||
/// "block_timestamp": u64::default(),
|
pub prunable_as_hex: String,
|
||||||
/// "confirmations": u64::default(),
|
pub prunable_hash: Hex<32>,
|
||||||
/// "double_spend_seen": bool::default(),
|
pub pruned_as_hex: String,
|
||||||
/// "output_indices": Vec::<u64>::default(),
|
|
||||||
/// "prunable_as_hex": String::default(),
|
#[cfg_attr(feature = "serde", serde(flatten))]
|
||||||
/// "prunable_hash": String::default(),
|
pub tx_entry_type: TxEntryType,
|
||||||
/// "pruned_as_hex": String::default(),
|
}
|
||||||
/// "tx_hash": String::default(),
|
|
||||||
/// "in_pool": bool::default(),
|
/// Different fields in [`TxEntry`] variants.
|
||||||
/// });
|
|
||||||
/// let tx_entry = from_value::<TxEntry>(json).unwrap();
|
|
||||||
/// assert!(matches!(tx_entry, TxEntry::InPool { .. }));
|
|
||||||
///
|
|
||||||
/// let json = json!({
|
|
||||||
/// "as_hex": String::default(),
|
|
||||||
/// "as_json": String::default(),
|
|
||||||
/// "double_spend_seen": bool::default(),
|
|
||||||
/// "prunable_as_hex": String::default(),
|
|
||||||
/// "prunable_hash": String::default(),
|
|
||||||
/// "pruned_as_hex": String::default(),
|
|
||||||
/// "received_timestamp": u64::default(),
|
|
||||||
/// "relayed": bool::default(),
|
|
||||||
/// "tx_hash": String::default(),
|
|
||||||
/// "in_pool": bool::default(),
|
|
||||||
/// });
|
|
||||||
/// let tx_entry = from_value::<TxEntry>(json).unwrap();
|
|
||||||
/// assert!(matches!(tx_entry, TxEntry::NotInPool { .. }));
|
|
||||||
/// ```
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(untagged))]
|
#[cfg_attr(feature = "serde", serde(untagged))]
|
||||||
pub enum TxEntry {
|
pub enum TxEntryType {
|
||||||
/// This entry exists in the transaction pool.
|
/// This transaction exists in the blockchain.
|
||||||
InPool {
|
Blockchain {
|
||||||
/// This field is [flattened](https://serde.rs/field-attrs.html#flatten).
|
|
||||||
#[cfg_attr(feature = "serde", serde(flatten))]
|
|
||||||
prefix: TxEntryPrefix,
|
|
||||||
block_height: u64,
|
block_height: u64,
|
||||||
block_timestamp: u64,
|
block_timestamp: u64,
|
||||||
confirmations: u64,
|
confirmations: u64,
|
||||||
output_indices: Vec<u64>,
|
output_indices: Vec<u64>,
|
||||||
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_true"))]
|
|
||||||
/// Will always be serialized as `true`.
|
/// Will always be serialized as `false`.
|
||||||
|
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_false"))]
|
||||||
in_pool: bool,
|
in_pool: bool,
|
||||||
},
|
},
|
||||||
/// This entry _does not_ exist in the transaction pool.
|
|
||||||
NotInPool {
|
/// This transaction exists in the transaction pool.
|
||||||
/// This field is [flattened](https://serde.rs/field-attrs.html#flatten).
|
Pool {
|
||||||
#[cfg_attr(feature = "serde", serde(flatten))]
|
|
||||||
prefix: TxEntryPrefix,
|
|
||||||
received_timestamp: u64,
|
received_timestamp: u64,
|
||||||
relayed: bool,
|
relayed: bool,
|
||||||
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_false"))]
|
|
||||||
/// Will always be serialized as `false`.
|
/// Will always be serialized as `true`.
|
||||||
|
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_true"))]
|
||||||
in_pool: bool,
|
in_pool: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TxEntry {
|
impl Default for TxEntryType {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::NotInPool {
|
Self::Blockchain {
|
||||||
prefix: Default::default(),
|
block_height: Default::default(),
|
||||||
received_timestamp: u64::default(),
|
block_timestamp: Default::default(),
|
||||||
relayed: bool::default(),
|
confirmations: Default::default(),
|
||||||
in_pool: false,
|
output_indices: Default::default(),
|
||||||
|
in_pool: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Common fields in all [`TxEntry`] variants.
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct TxEntryPrefix {
|
|
||||||
as_hex: String,
|
|
||||||
/// `cuprate_rpc_types::json::tx::Transaction` should be used
|
|
||||||
/// to create this JSON string in a type-safe manner.
|
|
||||||
as_json: String,
|
|
||||||
double_spend_seen: bool,
|
|
||||||
tx_hash: String,
|
|
||||||
prunable_as_hex: String,
|
|
||||||
prunable_hash: String,
|
|
||||||
pruned_as_hex: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Epee
|
//---------------------------------------------------------------------------------------------------- Epee
|
||||||
#[cfg(feature = "epee")]
|
#[cfg(feature = "epee")]
|
||||||
impl EpeeObjectBuilder<TxEntry> for () {
|
impl EpeeObjectBuilder<TxEntry> for () {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -134,6 +134,7 @@ fn map_request(
|
||||||
R::CoinbaseTxSum { height, count } => coinbase_tx_sum(env, height, count),
|
R::CoinbaseTxSum { height, count } => coinbase_tx_sum(env, height, count),
|
||||||
R::AltChains => alt_chains(env),
|
R::AltChains => alt_chains(env),
|
||||||
R::AltChainCount => alt_chain_count(env),
|
R::AltChainCount => alt_chain_count(env),
|
||||||
|
R::Transactions { tx_hashes } => transactions(env, tx_hashes),
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SOMEDAY: post-request handling, run some code for each request? */
|
/* SOMEDAY: post-request handling, run some code for each request? */
|
||||||
|
@ -506,7 +507,7 @@ 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: HashSet<KeyImage>) -> ResponseResult {
|
fn key_images_spent(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);
|
||||||
|
@ -519,26 +520,13 @@ fn key_images_spent(env: &ConcreteEnv, key_images: HashSet<KeyImage>) -> Respons
|
||||||
key_image_exists(&key_image, tables.key_images())
|
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`.
|
// Collect results using `rayon`.
|
||||||
match key_images
|
Ok(BlockchainResponse::KeyImagesSpent(
|
||||||
.into_par_iter()
|
key_images
|
||||||
.map(key_image_exists)
|
.into_par_iter()
|
||||||
// If the result is either:
|
.map(key_image_exists)
|
||||||
// `Ok(true)` => a key image was found, return early
|
.collect::<DbResult<_>>()?,
|
||||||
// `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::CompactChainHistory`]
|
/// [`BlockchainReadRequest::CompactChainHistory`]
|
||||||
|
@ -783,3 +771,11 @@ fn alt_chains(env: &ConcreteEnv) -> ResponseResult {
|
||||||
fn alt_chain_count(env: &ConcreteEnv) -> ResponseResult {
|
fn alt_chain_count(env: &ConcreteEnv) -> ResponseResult {
|
||||||
Ok(BlockchainResponse::AltChainCount(todo!()))
|
Ok(BlockchainResponse::AltChainCount(todo!()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`BlockchainReadRequest::Transactions`]
|
||||||
|
fn transactions(env: &ConcreteEnv, tx_hashes: HashSet<[u8; 32]>) -> ResponseResult {
|
||||||
|
Ok(BlockchainResponse::Transactions {
|
||||||
|
txs: todo!(),
|
||||||
|
missed_txs: todo!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -179,8 +179,8 @@ async fn test_template(
|
||||||
));
|
));
|
||||||
|
|
||||||
// Contains a fake non-spent key-image.
|
// Contains a fake non-spent key-image.
|
||||||
let ki_req = HashSet::from([[0; 32]]);
|
let ki_req = vec![[0; 32]];
|
||||||
let ki_resp = Ok(BlockchainResponse::KeyImagesSpent(false));
|
let ki_resp = Ok(BlockchainResponse::KeyImagesSpent(vec![false]));
|
||||||
|
|
||||||
//----------------------------------------------------------------------- Assert expected response
|
//----------------------------------------------------------------------- Assert expected response
|
||||||
// Assert read requests lead to the expected responses.
|
// Assert read requests lead to the expected responses.
|
||||||
|
@ -232,10 +232,13 @@ 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(HashSet::from([key_image]));
|
let request = BlockchainReadRequest::KeyImagesSpent(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!(response.unwrap(), BlockchainResponse::KeyImagesSpent(true));
|
assert_eq!(
|
||||||
|
response.unwrap(),
|
||||||
|
BlockchainResponse::KeyImagesSpent(vec![true])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------- Output checks
|
//----------------------------------------------------------------------- Output checks
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use cuprate_types::{rpc::PoolInfo, TransactionVerificationData};
|
use cuprate_types::{rpc::PoolInfo, TransactionVerificationData, TxInPool};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
tx::TxEntry,
|
tx::TxEntry,
|
||||||
|
@ -52,6 +52,18 @@ pub enum TxpoolReadRequest {
|
||||||
/// TODO
|
/// TODO
|
||||||
start_time: Option<NonZero<usize>>,
|
start_time: Option<NonZero<usize>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
TxsByHash {
|
||||||
|
tx_hashes: Vec<[u8; 32]>,
|
||||||
|
include_sensitive_txs: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
KeyImagesSpent {
|
||||||
|
key_images: Vec<[u8; 32]>,
|
||||||
|
include_sensitive_txs: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- TxpoolReadResponse
|
//---------------------------------------------------------------------------------------------------- TxpoolReadResponse
|
||||||
|
@ -93,7 +105,13 @@ pub enum TxpoolReadResponse {
|
||||||
Size(usize),
|
Size(usize),
|
||||||
|
|
||||||
/// Response to [`TxpoolReadRequest::PoolInfo`].
|
/// Response to [`TxpoolReadRequest::PoolInfo`].
|
||||||
PoolInfo(Vec<PoolInfo>),
|
PoolInfo(PoolInfo),
|
||||||
|
|
||||||
|
/// Response to [`TxpoolReadRequest::TxsByHash`].
|
||||||
|
TxsByHash(Vec<TxInPool>),
|
||||||
|
|
||||||
|
/// Response to [`TxpoolReadRequest::KeyImagesSpent`].
|
||||||
|
KeyImagesSpent(Vec<bool>),
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- TxpoolWriteRequest
|
//---------------------------------------------------------------------------------------------------- TxpoolWriteRequest
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
unreachable_code,
|
unreachable_code,
|
||||||
unused_variables,
|
unused_variables,
|
||||||
clippy::unnecessary_wraps,
|
clippy::unnecessary_wraps,
|
||||||
|
clippy::needless_pass_by_value,
|
||||||
reason = "TODO: finish implementing the signatures from <https://github.com/Cuprate/cuprate/pull/297>"
|
reason = "TODO: finish implementing the signatures from <https://github.com/Cuprate/cuprate/pull/297>"
|
||||||
)]
|
)]
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -79,7 +80,15 @@ fn map_request(
|
||||||
include_sensitive_txs,
|
include_sensitive_txs,
|
||||||
max_tx_count,
|
max_tx_count,
|
||||||
start_time,
|
start_time,
|
||||||
} => pool_info(include_sensitive_txs, max_tx_count, start_time),
|
} => pool_info(env, include_sensitive_txs, max_tx_count, start_time),
|
||||||
|
TxpoolReadRequest::TxsByHash {
|
||||||
|
tx_hashes,
|
||||||
|
include_sensitive_txs,
|
||||||
|
} => txs_by_hash(env, tx_hashes, include_sensitive_txs),
|
||||||
|
TxpoolReadRequest::KeyImagesSpent {
|
||||||
|
key_images,
|
||||||
|
include_sensitive_txs,
|
||||||
|
} => key_images_spent(env, key_images, include_sensitive_txs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,9 +224,28 @@ fn size(env: &ConcreteEnv, include_sensitive_txs: bool) -> ReadResponseResult {
|
||||||
|
|
||||||
/// [`TxpoolReadRequest::PoolInfo`].
|
/// [`TxpoolReadRequest::PoolInfo`].
|
||||||
fn pool_info(
|
fn pool_info(
|
||||||
|
env: &ConcreteEnv,
|
||||||
include_sensitive_txs: bool,
|
include_sensitive_txs: bool,
|
||||||
max_tx_count: usize,
|
max_tx_count: usize,
|
||||||
start_time: Option<NonZero<usize>>,
|
start_time: Option<NonZero<usize>>,
|
||||||
) -> ReadResponseResult {
|
) -> ReadResponseResult {
|
||||||
Ok(TxpoolReadResponse::PoolInfo(todo!()))
|
Ok(TxpoolReadResponse::PoolInfo(todo!()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [`TxpoolReadRequest::TxsByHash`].
|
||||||
|
fn txs_by_hash(
|
||||||
|
env: &ConcreteEnv,
|
||||||
|
tx_hashes: Vec<[u8; 32]>,
|
||||||
|
include_sensitive_txs: bool,
|
||||||
|
) -> ReadResponseResult {
|
||||||
|
Ok(TxpoolReadResponse::TxsByHash(todo!()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`TxpoolReadRequest::KeyImagesSpent`].
|
||||||
|
fn key_images_spent(
|
||||||
|
env: &ConcreteEnv,
|
||||||
|
key_images: Vec<[u8; 32]>,
|
||||||
|
include_sensitive_txs: bool,
|
||||||
|
) -> ReadResponseResult {
|
||||||
|
Ok(TxpoolReadResponse::KeyImagesSpent(todo!()))
|
||||||
|
}
|
||||||
|
|
|
@ -11,12 +11,11 @@ use std::{
|
||||||
use monero_serai::block::Block;
|
use monero_serai::block::Block;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
rpc::{ChainInfo, CoinbaseTxSum, OutputHistogramEntry, OutputHistogramInput},
|
||||||
types::{Chain, ExtendedBlockHeader, OutputOnChain, TxsInBlock, VerifiedBlockInformation},
|
types::{Chain, ExtendedBlockHeader, OutputOnChain, TxsInBlock, VerifiedBlockInformation},
|
||||||
AltBlockInformation, BlockCompleteEntry, ChainId,
|
AltBlockInformation, BlockCompleteEntry, ChainId, TxInBlockchain,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::rpc::{ChainInfo, CoinbaseTxSum, OutputHistogramEntry, OutputHistogramInput};
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- ReadRequest
|
//---------------------------------------------------------------------------------------------------- ReadRequest
|
||||||
/// A read request to the blockchain database.
|
/// A read request to the blockchain database.
|
||||||
///
|
///
|
||||||
|
@ -94,10 +93,10 @@ pub enum BlockchainReadRequest {
|
||||||
/// The input is a list of output amounts.
|
/// The input is a list of output amounts.
|
||||||
NumberOutputsWithAmount(Vec<u64>),
|
NumberOutputsWithAmount(Vec<u64>),
|
||||||
|
|
||||||
/// Check that all key images within a set are not spent.
|
/// Check the spend status of key images.
|
||||||
///
|
///
|
||||||
/// Input is a set of key images.
|
/// Input is a set of key images.
|
||||||
KeyImagesSpent(HashSet<[u8; 32]>),
|
KeyImagesSpent(Vec<[u8; 32]>),
|
||||||
|
|
||||||
/// A request for the compact chain history.
|
/// A request for the compact chain history.
|
||||||
CompactChainHistory,
|
CompactChainHistory,
|
||||||
|
@ -161,6 +160,9 @@ pub enum BlockchainReadRequest {
|
||||||
|
|
||||||
/// Get the amount of alternative chains that exist.
|
/// Get the amount of alternative chains that exist.
|
||||||
AltChainCount,
|
AltChainCount,
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
Transactions { tx_hashes: HashSet<[u8; 32]> },
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- WriteRequest
|
//---------------------------------------------------------------------------------------------------- WriteRequest
|
||||||
|
@ -268,11 +270,11 @@ pub enum BlockchainResponse {
|
||||||
|
|
||||||
/// Response to [`BlockchainReadRequest::KeyImagesSpent`].
|
/// Response to [`BlockchainReadRequest::KeyImagesSpent`].
|
||||||
///
|
///
|
||||||
/// The inner value is `true` if _any_ of the key images
|
/// Inner value is a `Vec` the same length as the input.
|
||||||
/// were spent (existed in the database already).
|
|
||||||
///
|
///
|
||||||
/// The inner value is `false` if _none_ of the key images were spent.
|
/// The index of each entry corresponds with the request.
|
||||||
KeyImagesSpent(bool),
|
/// `true` means that the key image was spent.
|
||||||
|
KeyImagesSpent(Vec<bool>),
|
||||||
|
|
||||||
/// Response to [`BlockchainReadRequest::CompactChainHistory`].
|
/// Response to [`BlockchainReadRequest::CompactChainHistory`].
|
||||||
CompactChainHistory {
|
CompactChainHistory {
|
||||||
|
@ -348,6 +350,14 @@ pub enum BlockchainResponse {
|
||||||
/// Response to [`BlockchainReadRequest::AltChainCount`].
|
/// Response to [`BlockchainReadRequest::AltChainCount`].
|
||||||
AltChainCount(usize),
|
AltChainCount(usize),
|
||||||
|
|
||||||
|
/// Response to [`BlockchainReadRequest::Transactions`].
|
||||||
|
Transactions {
|
||||||
|
/// The transaction blobs found.
|
||||||
|
txs: Vec<TxInBlockchain>,
|
||||||
|
/// The hashes of any transactions that could not be found.
|
||||||
|
missed_txs: Vec<[u8; 32]>,
|
||||||
|
},
|
||||||
|
|
||||||
//------------------------------------------------------ Writes
|
//------------------------------------------------------ Writes
|
||||||
/// A generic Ok response to indicate a request was successfully handled.
|
/// A generic Ok response to indicate a request was successfully handled.
|
||||||
///
|
///
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub use transaction_verification_data::{
|
||||||
};
|
};
|
||||||
pub use types::{
|
pub use types::{
|
||||||
AltBlockInformation, BlockTemplate, Chain, ChainId, ExtendedBlockHeader, OutputOnChain,
|
AltBlockInformation, BlockTemplate, Chain, ChainId, ExtendedBlockHeader, OutputOnChain,
|
||||||
TxsInBlock, VerifiedBlockInformation, VerifiedTransactionInformation,
|
TxInBlockchain, TxInPool, TxsInBlock, VerifiedBlockInformation, VerifiedTransactionInformation,
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Feature-gated
|
//---------------------------------------------------------------------------------------------------- Feature-gated
|
||||||
|
|
|
@ -12,12 +12,7 @@ use cuprate_epee_encoding::{
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- KeyImageSpentStatus
|
//---------------------------------------------------------------------------------------------------- KeyImageSpentStatus
|
||||||
#[doc = crate::macros::monero_definition_link!(
|
/// Used in RPC's `/is_key_image_spent`.
|
||||||
"cc73fe71162d564ffda8e549b79a350bca53c454",
|
|
||||||
"rpc/core_rpc_server_commands_defs.h",
|
|
||||||
456..=460
|
|
||||||
)]
|
|
||||||
/// Used in [`crate::other::IsKeyImageSpentResponse`].
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))]
|
#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))]
|
||||||
|
@ -32,7 +27,7 @@ impl KeyImageSpentStatus {
|
||||||
/// Convert [`Self`] to a [`u8`].
|
/// Convert [`Self`] to a [`u8`].
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use cuprate_rpc_types::misc::KeyImageSpentStatus as K;
|
/// use cuprate_types::rpc::KeyImageSpentStatus as K;
|
||||||
///
|
///
|
||||||
/// assert_eq!(K::Unspent.to_u8(), 0);
|
/// assert_eq!(K::Unspent.to_u8(), 0);
|
||||||
/// assert_eq!(K::SpentInBlockchain.to_u8(), 1);
|
/// assert_eq!(K::SpentInBlockchain.to_u8(), 1);
|
||||||
|
@ -52,7 +47,7 @@ impl KeyImageSpentStatus {
|
||||||
/// This returns [`None`] if `u > 2`.
|
/// This returns [`None`] if `u > 2`.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use cuprate_rpc_types::misc::KeyImageSpentStatus as K;
|
/// use cuprate_types::rpc::KeyImageSpentStatus as K;
|
||||||
///
|
///
|
||||||
/// assert_eq!(K::from_u8(0), Some(K::Unspent));
|
/// assert_eq!(K::from_u8(0), Some(K::Unspent));
|
||||||
/// assert_eq!(K::from_u8(1), Some(K::SpentInBlockchain));
|
/// assert_eq!(K::from_u8(1), Some(K::SpentInBlockchain));
|
|
@ -5,10 +5,12 @@
|
||||||
//! instead of `hash: String`, this module's types would use something like
|
//! instead of `hash: String`, this module's types would use something like
|
||||||
//! `hash: [u8; 32]`.
|
//! `hash: [u8; 32]`.
|
||||||
|
|
||||||
|
mod key_image_spent_status;
|
||||||
mod pool_info;
|
mod pool_info;
|
||||||
mod pool_info_extent;
|
mod pool_info_extent;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
pub use key_image_spent_status::KeyImageSpentStatus;
|
||||||
pub use pool_info::PoolInfo;
|
pub use pool_info::PoolInfo;
|
||||||
pub use pool_info_extent::PoolInfoExtent;
|
pub use pool_info_extent::PoolInfoExtent;
|
||||||
pub use types::{
|
pub use types::{
|
||||||
|
|
|
@ -167,6 +167,30 @@ pub struct BlockTemplate {
|
||||||
pub next_seed_hash: [u8; 32],
|
pub next_seed_hash: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct TxInBlockchain {
|
||||||
|
pub block_height: u64,
|
||||||
|
pub block_timestamp: u64,
|
||||||
|
pub confirmations: u64,
|
||||||
|
pub output_indices: Vec<u64>,
|
||||||
|
pub tx_hash: [u8; 32],
|
||||||
|
pub tx_blob: Vec<u8>,
|
||||||
|
pub pruned_blob: Vec<u8>,
|
||||||
|
pub prunable_blob: Vec<u8>,
|
||||||
|
pub prunable_hash: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct TxInPool {
|
||||||
|
pub tx_blob: Vec<u8>,
|
||||||
|
pub tx_hash: [u8; 32],
|
||||||
|
pub double_spend_seen: bool,
|
||||||
|
pub received_timestamp: u64,
|
||||||
|
pub relayed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Tests
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
Loading…
Reference in a new issue