Compare commits

..

No commits in common. "b2b7e93e4cfbb9f5794913aa811cbaf96ca04fcc" and "ed8de29504d1709626f201a07db8a4f1227d7d30" have entirely different histories.

17 changed files with 541 additions and 912 deletions

866
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -23,7 +23,7 @@ use cuprate_helper::{
fmt::HexPrefix,
map::split_u128_into_low_high_bits,
};
use cuprate_hex::{Hex, HexVec};
use cuprate_hex::Hex;
use cuprate_p2p_core::{client::handshaker::builder::DummyAddressBook, ClearNet, Network};
use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{
@ -54,7 +54,7 @@ use cuprate_rpc_types::{
};
use cuprate_types::{
rpc::{AuxPow, CoinbaseTxSum, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry},
BlockTemplate, Chain, HardFork,
BlockTemplate, HardFork,
};
use crate::{
@ -162,7 +162,7 @@ async fn get_block_template(
}
let blob_reserve = hex::decode(request.extra_nonce)?;
let prev_block: [u8; 32] = request.prev_block.try_into()?;
let prev_block = helper::hex_to_hash(request.prev_block)?;
let extra_nonce = hex::decode(request.extra_nonce)?;
let BlockTemplate {
@ -182,11 +182,11 @@ async fn get_block_template(
)
.await?;
let blockhashing_blob = HexVec(block.serialize_pow_hash());
let blocktemplate_blob = HexVec(block.serialize());
let blockhashing_blob = hex::encode(block.serialize_pow_hash());
let blocktemplate_blob = hex::encode(block.serialize());
let (difficulty, difficulty_top64) = split_u128_into_low_high_bits(difficulty);
// let next_seed_hash = Hex(next_seed_hash);
let next_seed_hash = HexVec::empty_if_zeroed(next_seed_hash);
let next_seed_hash = hex::encode(next_seed_hash);
let prev_hash = Hex(block.header.previous);
let seed_hash = Hex(seed_hash);
let wide_difficulty = (difficulty, difficulty_top64).hex_prefix();
@ -215,10 +215,7 @@ async fn get_block_count(
) -> Result<GetBlockCountResponse, Error> {
Ok(GetBlockCountResponse {
base: helper::response_base(false),
// Block count starts at 1
count: blockchain::chain_height(&mut state.blockchain_read)
.await?
.0,
count: helper::top_height(&mut state).await?.0,
})
}
@ -228,7 +225,12 @@ async fn on_get_block_hash(
request: OnGetBlockHashRequest,
) -> Result<OnGetBlockHashResponse, Error> {
let [height] = request.block_height;
let hash = blockchain::block_hash(&mut state.blockchain_read, height, Chain::Main).await?;
let hash = blockchain::block_hash(
&mut state.blockchain_read,
height,
todo!("access to `cuprated`'s Chain"),
)
.await?;
Ok(OnGetBlockHashResponse {
block_hash: Hex(hash),
@ -242,7 +244,8 @@ async fn submit_block(
) -> Result<SubmitBlockResponse, Error> {
// Parse hex into block.
let [blob] = request.block_blob;
let block = Block::read(&mut blob.0.as_slice())?;
let bytes = hex::decode(blob)?;
let block = Block::read(&mut bytes.as_slice())?;
let block_id = Hex(block.hash());
// Attempt to relay the block.
@ -269,7 +272,7 @@ async fn generate_blocks(
let prev_block = if request.prev_block.is_empty() {
None
} else {
Some(request.prev_block.try_into()?)
Some(helper::hex_to_hash(request.prev_block)?)
};
let (blocks, height) = blockchain_manager::generate_blocks(
@ -315,13 +318,21 @@ async fn get_block_header_by_hash(
));
}
let block_header =
helper::block_header_by_hash(&mut state, request.hash.0, request.fill_pow_hash).await?;
async fn get(
state: &mut CupratedRpcHandler,
hash: [u8; 32],
fill_pow_hash: bool,
) -> Result<BlockHeader, Error> {
let block_header = helper::block_header_by_hash(state, hash, fill_pow_hash).await?;
Ok(block_header)
}
let block_header = get(&mut state, request.hash.0, request.fill_pow_hash).await?;
// FIXME PERF: could make a `Vec` on await on all tasks at the same time.
let mut block_headers = Vec::with_capacity(request.hashes.len());
for hash in request.hashes {
let hash = helper::block_header_by_hash(&mut state, hash.0, request.fill_pow_hash).await?;
let hash = get(&mut state, hash.0, request.fill_pow_hash).await?;
block_headers.push(hash);
}
@ -414,16 +425,16 @@ async fn get_block(
helper::block_header(&mut state, request.height, request.fill_pow_hash).await?;
(block, block_header)
} else {
let hash: [u8; 32] = request.hash.try_into()?;
let hash = helper::hex_to_hash(request.hash)?;
let block = blockchain::block_by_hash(&mut state.blockchain_read, hash).await?;
let block_header =
helper::block_header_by_hash(&mut state, hash, request.fill_pow_hash).await?;
(block, block_header)
};
let blob = HexVec(block.serialize());
let blob = hex::encode(block.serialize());
let miner_tx_hash = Hex(block.miner_transaction.hash());
let tx_hashes = block.transactions.iter().copied().map(Hex).collect();
let tx_hashes = block.transactions.iter().map(|a| Hex(*a)).collect();
let json = {
let block = cuprate_types::json::block::Block::from(block);
serde_json::to_string_pretty(&block)?
@ -462,7 +473,8 @@ async fn get_info(
let c = context.unchecked_blockchain_context();
let cumulative_difficulty = c.cumulative_difficulty;
let adjusted_time = c.current_adjusted_timestamp_for_time_lock();
let adjusted_time = c.current_adjusted_timestamp_for_time_lock(); // TODO: is this correct?
let c = &c.context_to_verify_block;
let alt_blocks_count = if restricted {
@ -470,98 +482,76 @@ async fn get_info(
} else {
blockchain::alt_chain_count(&mut state.blockchain_read).await?
};
let block_weight_limit = usize_to_u64(c.effective_median_weight);
let block_weight_median = usize_to_u64(c.median_weight_for_block_reward);
let block_weight_limit = usize_to_u64(c.effective_median_weight); // TODO: is this correct?
let block_weight_median = usize_to_u64(c.median_weight_for_block_reward); // TODO: is this correct?
let block_size_limit = block_weight_limit;
let block_size_median = block_weight_median;
#[expect(clippy::if_same_then_else, reason = "TODO: support bootstrap")]
let (bootstrap_daemon_address, was_bootstrap_ever_used) = if restricted {
(String::new(), false)
} else {
(String::new(), false)
todo!("support bootstrap daemon")
};
let busy_syncing = blockchain_manager::syncing(&mut state.blockchain_manager).await?;
let (cumulative_difficulty, cumulative_difficulty_top64) =
split_u128_into_low_high_bits(cumulative_difficulty);
let (database_size, free_space) = blockchain::database_size(&mut state.blockchain_read).await?;
let (database_size, free_space) = if restricted {
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L131-L134>
const fn round_up(value: u64, quantum: u64) -> u64 {
value.div_ceil(quantum)
}
let database_size = round_up(database_size, 5 * 1024 * 1024 * 1024);
(database_size, u64::MAX)
} else {
(database_size, free_space)
};
let (difficulty, difficulty_top64) = split_u128_into_low_high_bits(c.next_difficulty);
let height = usize_to_u64(c.chain_height);
let height_without_bootstrap = if restricted { 0 } else { height };
let (incoming_connections_count, outgoing_connections_count) = if restricted {
(0, 0)
} else {
address_book::connection_count::<ClearNet>(&mut DummyAddressBook).await?
};
// TODO: This should be `cuprated`'s active network.
let network = Network::Mainnet;
let (mainnet, testnet, stagenet) = match network {
let (mainnet, testnet, stagenet) = match todo!("access to `cuprated`'s `Network`") {
Network::Mainnet => (true, false, false),
Network::Testnet => (false, true, false),
Network::Stagenet => (false, false, true),
};
let nettype = network.to_string();
// TODO: access to CLI/config's `--offline`
let offline = false;
#[expect(
clippy::if_same_then_else,
reason = "TODO: implement a connection counter in axum/RPC"
)]
let rpc_connections_count = if restricted { 0 } else { 0 };
// TODO: make sure this is:
// - the same case as `monerod`
// - untagged (no `Network::`)
let nettype = todo!("access to `cuprated`'s `Network`").to_string();
let offline = todo!("access to CLI/config's `--offline`");
let rpc_connections_count = if restricted {
0
} else {
todo!("implement a connection counter in axum/RPC")
};
let start_time = if restricted { 0 } else { *START_INSTANT_UNIX };
let synchronized = blockchain_manager::synced(&mut state.blockchain_manager).await?;
let target_height = blockchain_manager::target_height(&mut state.blockchain_manager).await?;
let target = blockchain_manager::target(&mut state.blockchain_manager)
.await?
.as_secs();
let top_block_hash = Hex(c.top_hash);
let tx_count = blockchain::total_tx_count(&mut state.blockchain_read).await?;
let tx_pool_size = txpool::size(&mut state.txpool_read, !restricted).await?;
#[expect(
clippy::if_same_then_else,
clippy::needless_bool,
reason = "TODO: implement an update checker for `cuprated`?"
)]
let update_available = if restricted { false } else { false };
let update_available = if restricted {
false
} else {
todo!("implement an update checker for `cuprated`")
};
let version = if restricted {
String::new()
} else {
VERSION_BUILD.to_string()
};
let (white_peerlist_size, grey_peerlist_size) = if restricted {
(0, 0)
} else {
address_book::peerlist_size::<ClearNet>(&mut DummyAddressBook).await?
};
let wide_cumulative_difficulty = format!("{cumulative_difficulty:#x}");
let wide_difficulty = format!("{:#x}", c.next_difficulty);
@ -623,12 +613,18 @@ async fn hard_fork_info(
.current_hf
};
let hard_fork_info =
blockchain_context::hard_fork_info(&mut state.blockchain_context, hard_fork).await?;
let info = blockchain_context::hard_fork_info(&mut state.blockchain_context, hard_fork).await?;
Ok(HardForkInfoResponse {
base: helper::access_response_base(false),
hard_fork_info,
earliest_height: info.earliest_height,
enabled: info.enabled,
state: info.state,
threshold: info.threshold,
version: info.version,
votes: info.votes,
voting: info.voting,
window: info.window,
})
}
@ -1028,7 +1024,8 @@ async fn calc_pow(
request: CalcPowRequest,
) -> Result<CalcPowResponse, Error> {
let hardfork = HardFork::from_version(request.major_version)?;
let block = Block::read(&mut request.block_blob.0.as_slice())?;
let block_blob: Vec<u8> = hex::decode(request.block_blob)?;
let block = Block::read(&mut block_blob.as_slice())?;
let seed_hash = request.seed_hash.0;
// let block_weight = todo!();
@ -1046,7 +1043,6 @@ async fn calc_pow(
// }
// TODO: will `CalculatePow` do the above checks?
let pow_hash = blockchain_context::calculate_pow(
&mut state.blockchain_context,
hardfork,
@ -1060,16 +1056,23 @@ async fn calc_pow(
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3542-L3551>
async fn flush_cache(
state: CupratedRpcHandler,
request: FlushCacheRequest,
) -> Result<FlushCacheResponse, Error> {
// TODO: cuprated doesn't need this call; decide behavior.
Ok(FlushCacheResponse {
base: helper::response_base(false),
})
}
/// An async-friendly wrapper for [`add_aux_pow_inner`].
async fn add_aux_pow(
state: CupratedRpcHandler,
request: AddAuxPowRequest,
) -> Result<AddAuxPowResponse, Error> {
// This method can be a bit heavy, so rate-limit restricted use.
if state.is_restricted() {
tokio::time::sleep(Duration::from_millis(100)).await;
}
tokio::task::spawn_blocking(|| add_aux_pow_inner(state, request)).await?
}
@ -1219,7 +1222,10 @@ fn add_aux_pow_inner(
let merkle_root = tree_hash(aux_pow_raw.as_ref());
let merkle_tree_depth = encode_mm_depth(len, nonce);
let block_template = Block::read(&mut request.blocktemplate_blob.0.as_slice())?;
let block_template = {
let hex = hex::decode(request.blocktemplate_blob)?;
Block::read(&mut hex.as_slice())?
};
fn remove_field_from_tx_extra() -> Result<(), ()> {
todo!("https://github.com/monero-project/monero/blob/master/src/cryptonote_basic/cryptonote_format_utils.cpp#L767")
@ -1250,8 +1256,8 @@ fn add_aux_pow_inner(
let blocktemplate_blob = block_template.serialize();
let blockhashing_blob = block_template.serialize_pow_hash();
let blocktemplate_blob = HexVec(blocktemplate_blob);
let blockhashing_blob = HexVec(blockhashing_blob);
let blocktemplate_blob = hex::encode(blocktemplate_blob);
let blockhashing_blob = hex::encode(blockhashing_blob);
let merkle_root = Hex(merkle_root);
let aux_pow = aux_pow.into_vec();
@ -1265,25 +1271,16 @@ fn add_aux_pow_inner(
})
}
//---------------------------------------------------------------------------------------------------- Unsupported RPC calls (for now)
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3553-L3627>
async fn get_tx_ids_loose(
state: CupratedRpcHandler,
request: GetTxIdsLooseRequest,
) -> Result<GetTxIdsLooseResponse, Error> {
// TODO: this RPC call is not yet in the v0.18 branch.
return Err(anyhow!("Not implemented"));
Ok(GetTxIdsLooseResponse {
base: helper::response_base(false),
txids: todo!("this RPC call is not yet in the v0.18 branch."),
txids: todo!(),
})
}
//---------------------------------------------------------------------------------------------------- Unsupported RPC calls (forever)
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3542-L3551>
async fn flush_cache(
state: CupratedRpcHandler,
request: FlushCacheRequest,
) -> Result<FlushCacheResponse, Error> {
unreachable!()
}

View file

@ -12,7 +12,7 @@ use cuprate_constants::rpc::{
RESTRICTED_TRANSACTIONS_COUNT,
};
use cuprate_helper::cast::usize_to_u64;
use cuprate_hex::{Hex, HexVec};
use cuprate_hex::Hex;
use cuprate_p2p_core::{client::handshaker::builder::DummyAddressBook, ClearNet};
use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{
@ -157,23 +157,23 @@ async fn get_transactions(
let prunable_hash = Hex(tx.prunable_hash);
let (pruned_as_hex, prunable_as_hex) = if tx.pruned_blob.is_empty() {
(HexVec::new(), HexVec::new())
(String::new(), String::new())
} else {
(HexVec(tx.pruned_blob), HexVec(tx.prunable_blob))
(hex::encode(tx.pruned_blob), hex::encode(tx.prunable_blob))
};
let as_hex = if pruned_as_hex.is_empty() {
// `monerod` will insert a `""` into the `txs_as_hex` array for pruned transactions.
// curl http://127.0.0.1:18081/get_transactions -d '{"txs_hashes":["4c8b98753d1577d225a497a50f453827cff3aa023a4add60ec4ce4f923f75de8"]}' -H 'Content-Type: application/json'
HexVec::new()
String::new()
} else {
HexVec(tx.tx_blob)
hex::encode(&tx.tx_blob)
};
txs_as_hex.push(as_hex.clone());
let as_json = if request.decode_as_json {
let tx = Transaction::read(&mut as_hex.0.as_slice())?;
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());
@ -217,11 +217,11 @@ async fn get_transactions(
let tx_hash = Hex(tx_hash);
let tx = Transaction::read(&mut tx_blob.as_slice())?;
let pruned_as_hex = HexVec::new();
let prunable_as_hex = HexVec::new();
let pruned_as_hex = String::new();
let prunable_as_hex = String::new();
let prunable_hash = Hex([0; 32]);
let as_hex = HexVec(tx_blob);
let as_hex = hex::encode(tx_blob);
txs_as_hex.push(as_hex.clone());
let as_json = if request.decode_as_json {
@ -351,7 +351,10 @@ async fn send_raw_transaction(
tx_extra_too_big: false,
};
let tx = Transaction::read(&mut request.tx_as_hex.0.as_slice())?;
let tx = {
let blob = hex::decode(request.tx_as_hex)?;
Transaction::read(&mut blob.as_slice())?
};
if request.do_sanity_checks {
/// FIXME: these checks could be defined elsewhere.

View file

@ -59,7 +59,7 @@ pub async fn size(
Ok(usize_to_u64(size))
}
/// [`TxpoolReadRequest::PoolInfo`]
/// TODO
pub async fn pool_info(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
@ -84,7 +84,7 @@ pub async fn pool_info(
Ok(pool_info)
}
/// [`TxpoolReadRequest::TxsByHash`]
/// TODO
pub async fn txs_by_hash(
txpool_read: &mut TxpoolReadHandle,
tx_hashes: Vec<[u8; 32]>,
@ -107,7 +107,7 @@ pub async fn txs_by_hash(
Ok(txs_in_pool)
}
/// [`TxpoolReadRequest::KeyImagesSpent`]
/// TODO
pub async fn key_images_spent(
txpool_read: &mut TxpoolReadHandle,
key_images: Vec<[u8; 32]>,
@ -130,7 +130,7 @@ pub async fn key_images_spent(
Ok(status)
}
/// [`TxpoolReadRequest::Pool`]
/// TODO
pub async fn pool(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
@ -157,7 +157,7 @@ pub async fn pool(
Ok((txs, spent_key_images))
}
/// [`TxpoolReadRequest::PoolStats`]
/// TODO
pub async fn pool_stats(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
@ -178,7 +178,7 @@ pub async fn pool_stats(
Ok(txpool_stats)
}
/// [`TxpoolReadRequest::AllHashes`]
/// TODO
pub async fn all_hashes(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
@ -199,19 +199,19 @@ pub async fn all_hashes(
Ok(hashes)
}
/// TODO: impl txpool manager.
/// TODO
pub async fn flush(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> {
todo!();
Ok(())
}
/// TODO: impl txpool manager.
/// TODO
pub async fn relay(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> {
todo!();
Ok(())
}
/// TODO: impl txpool manager.
/// TODO
pub async fn check_maybe_relay_local(
txpool_manager: &mut Infallible,
tx: Transaction,

View file

@ -8,7 +8,7 @@ use bytes::{Buf, BufMut, Bytes, BytesMut};
use cuprate_fixed_bytes::{ByteArray, ByteArrayVec};
use cuprate_helper::cast::u64_to_usize;
use cuprate_hex::{Hex, HexVec};
use cuprate_hex::Hex;
use crate::{
io::{checked_read_primitive, checked_write_primitive},
@ -437,18 +437,6 @@ impl<const N: usize> EpeeValue for Vec<Hex<N>> {
}
}
impl EpeeValue for HexVec {
const MARKER: Marker = <Vec<u8> as EpeeValue>::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> Result<Self> {
Ok(Self(<Vec<u8> as EpeeValue>::read(r, marker)?))
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
<Vec<u8> as EpeeValue>::write(self.0, w)
}
}
macro_rules! epee_seq {
($val:ty) => {
impl EpeeValue for Vec<$val> {
@ -515,7 +503,6 @@ epee_seq!(u16);
epee_seq!(f64);
epee_seq!(bool);
epee_seq!(Vec<u8>);
epee_seq!(HexVec);
epee_seq!(String);
epee_seq!(Bytes);
epee_seq!(BytesMut);

View file

@ -8,7 +8,7 @@ use std::{
};
use cuprate_helper::{fmt::HexPrefix, map::ipv4_from_u32};
use cuprate_hex::{Hex, HexVec};
use cuprate_hex::Hex;
use cuprate_p2p_core::{
types::{ConnectionId, ConnectionInfo, SetBan, Span},
NetZoneAddress,
@ -34,7 +34,7 @@ impl From<BlockHeader> for crate::misc::BlockHeader {
nonce: x.nonce,
num_txes: x.num_txes,
orphan_status: x.orphan_status,
pow_hash: x.pow_hash.map_or_else(HexVec::new, |a| HexVec(a.into())),
pow_hash: x.pow_hash.map_or_else(String::new, hex::encode),
prev_hash: Hex(x.prev_hash),
reward: x.reward,
timestamp: x.timestamp,
@ -178,7 +178,7 @@ impl From<TxInfo> for crate::misc::TxInfo {
max_used_block_id_hash: Hex(x.max_used_block_id_hash),
receive_time: x.receive_time,
relayed: x.relayed,
tx_blob: HexVec(x.tx_blob),
tx_blob: hex::encode(x.tx_blob),
tx_json: x.tx_json,
weight: x.weight,
}
@ -201,11 +201,7 @@ impl From<crate::misc::OutKeyBin> for crate::misc::OutKey {
mask: Hex(x.mask),
unlocked: x.unlocked,
height: x.height,
txid: if x.txid == [0; 32] {
HexVec::new()
} else {
HexVec::from(x.txid)
},
txid: hex::encode(x.txid),
}
}
}

View file

@ -6,10 +6,8 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use cuprate_hex::{Hex, HexVec};
use cuprate_types::rpc::{
AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, HardForkInfo, TxBacklogEntry,
};
use cuprate_hex::Hex;
use cuprate_types::rpc::{AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry};
use crate::{
base::{AccessResponseBase, ResponseBase},
@ -81,7 +79,7 @@ define_request_and_response! {
// This is a HACK since `serde`'s default attribute only takes in
// string literals and macros (stringify) within attributes do not work.
extra_nonce: String = default::<String>(), "default",
prev_block: HexVec = default::<HexVec>(), "default",
prev_block: String = default::<String>(), "default",
// Another optional expression:
// This indicates to the macro to (de)serialize
@ -118,14 +116,13 @@ define_request_and_response! {
// status: crate::Status,
// untrusted: bool,
// ```
blockhashing_blob: HexVec,
blocktemplate_blob: HexVec,
blockhashing_blob: String,
blocktemplate_blob: String,
difficulty_top64: u64,
difficulty: u64,
expected_reward: u64,
height: u64,
/// This is a [`Hex<32>`] that is sometimes empty.
next_seed_hash: HexVec,
next_seed_hash: String,
prev_hash: Hex<32>,
reserved_offset: u64,
seed_hash: Hex<32>,
@ -182,7 +179,7 @@ define_request_and_response! {
Request {
// This is `std::vector<std::string>` in `monerod` but
// it must be a 1 length array or else it will error.
block_blob: [HexVec; 1],
block_blob: [String; 1],
},
// FIXME: `cuprate_test_utils` only has an `error` response for this.
@ -200,7 +197,7 @@ define_request_and_response! {
Request {
amount_of_blocks: u64,
prev_block: HexVec = default::<HexVec>(), "default",
prev_block: String = default::<String>(), "default",
starting_nonce: u32,
wallet_address: String,
},
@ -293,15 +290,13 @@ define_request_and_response! {
// `monerod` has both `hash` and `height` fields.
// In the RPC handler, if `hash.is_empty()`, it will use it, else, it uses `height`.
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2674>
/// This is a [`Hex<32>`] that is sometimes empty.
hash: HexVec = default::<HexVec>(), "default",
hash: String = default::<String>(), "default",
height: u64 = default::<u64>(), "default",
fill_pow_hash: bool = default::<bool>(), "default",
},
AccessResponseBase {
blob: HexVec,
blob: String,
block_header: BlockHeader,
/// `cuprate_types::json::block::Block` should be used
/// to create this JSON string in a type-safe manner.
@ -387,9 +382,14 @@ define_request_and_response! {
},
AccessResponseBase {
/// This field is [flattened](https://serde.rs/field-attrs.html#flatten).
#[cfg_attr(feature = "serde", serde(flatten))]
hard_fork_info: HardForkInfo,
earliest_height: u64,
enabled: bool,
state: u32,
threshold: u32,
version: u8,
votes: u32,
voting: u8,
window: u32,
}
}
@ -662,7 +662,7 @@ define_request_and_response! {
Request {
major_version: u8,
height: u64,
block_blob: HexVec,
block_blob: String,
seed_hash: Hex<32>,
},
@ -697,13 +697,13 @@ define_request_and_response! {
AddAuxPow,
Request {
blocktemplate_blob: HexVec,
blocktemplate_blob: String,
aux_pow: Vec<AuxPow>,
},
ResponseBase {
blocktemplate_blob: HexVec,
blockhashing_blob: HexVec,
blocktemplate_blob: String,
blockhashing_blob: String,
merkle_root: Hex<32>,
merkle_tree_depth: u64,
aux_pow: Vec<AuxPow>,
@ -722,7 +722,7 @@ define_request_and_response! {
num_matching_bits: u32,
},
ResponseBase {
txids: Vec<Hex<32>>,
txids: Vec<String>,
}
}
@ -959,7 +959,7 @@ mod test {
test_json_request(json::GET_BLOCK_TEMPLATE_REQUEST, GetBlockTemplateRequest {
reserve_size: 60,
extra_nonce: String::default(),
prev_block: HexVec::default(),
prev_block: String::default(),
wallet_address: "44GBHzv6ZyQdJkjqZje6KLZ3xSyN1hBSFAnLP6EAqJtCRVzMzZmeXTC2AHKDS9aEDTRKmo6a6o9r9j86pYfhCWDkKjbtcns".into(),
});
}
@ -968,13 +968,13 @@ mod test {
fn get_block_template_response() {
test_json_response(json::GET_BLOCK_TEMPLATE_RESPONSE, GetBlockTemplateResponse {
base: ResponseBase::OK,
blockhashing_blob: HexVec(hex!("1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a00000000e0c20372be23d356347091025c5b5e8f2abf83ab618378565cce2b703491523401").into()),
blocktemplate_blob: HexVec(hex!("1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()),
blockhashing_blob: "1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a00000000e0c20372be23d356347091025c5b5e8f2abf83ab618378565cce2b703491523401".into(),
blocktemplate_blob: "1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(),
difficulty_top64: 0,
difficulty: 283305047039,
expected_reward: 600000000000,
height: 3195018,
next_seed_hash: HexVec::new(),
next_seed_hash: String::new(),
prev_hash: Hex(hex!("9d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a")),
reserved_offset: 131,
seed_hash: Hex(hex!("e2aa0b7b55042cd48b02e395d78fa66a29815ccc1584e38db2d1f0e8485cd44f")),
@ -1019,7 +1019,7 @@ mod test {
#[test]
fn submit_block_request() {
test_json_request(json::SUBMIT_BLOCK_REQUEST, SubmitBlockRequest {
block_blob: [HexVec(hex!("0707e6bdfedc053771512f1bc27c62731ae9e8f2443db64ce742f4e57f5cf8d393de28551e441a0000000002fb830a01ffbf830a018cfe88bee283060274c0aae2ef5730e680308d9c00b6da59187ad0352efe3c71d36eeeb28782f29f2501bd56b952c3ddc3e350c2631d3a5086cac172c56893831228b17de296ff4669de020200000000").into())],
block_blob: ["0707e6bdfedc053771512f1bc27c62731ae9e8f2443db64ce742f4e57f5cf8d393de28551e441a0000000002fb830a01ffbf830a018cfe88bee283060274c0aae2ef5730e680308d9c00b6da59187ad0352efe3c71d36eeeb28782f29f2501bd56b952c3ddc3e350c2631d3a5086cac172c56893831228b17de296ff4669de020200000000".into()],
});
}
@ -1027,7 +1027,7 @@ mod test {
fn generate_blocks_request() {
test_json_request(json::GENERATE_BLOCKS_REQUEST, GenerateBlocksRequest {
amount_of_blocks: 1,
prev_block: HexVec::default(),
prev_block: String::default(),
wallet_address: "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A".into(),
starting_nonce: 0
});
@ -1074,7 +1074,7 @@ mod test {
nonce: 1885649739,
num_txes: 37,
orphan_status: false,
pow_hash: HexVec::new(),
pow_hash: String::new(),
prev_hash: Hex(hex!(
"22c72248ae9c5a2863c94735d710a3525c499f70707d1c2f395169bc5c8a0da3"
)),
@ -1129,7 +1129,7 @@ mod test {
nonce: 1646,
num_txes: 0,
orphan_status: false,
pow_hash: HexVec::new(),
pow_hash: String::new(),
prev_hash: Hex(hex!(
"b61c58b2e0be53fad5ef9d9731a55e8a81d972b8d90ed07c04fd37ca6403ff78"
)),
@ -1180,7 +1180,7 @@ mod test {
nonce: 1646,
num_txes: 0,
orphan_status: false,
pow_hash: HexVec::new(),
pow_hash: String::new(),
prev_hash: Hex(hex!(
"b61c58b2e0be53fad5ef9d9731a55e8a81d972b8d90ed07c04fd37ca6403ff78"
)),
@ -1233,7 +1233,7 @@ mod test {
nonce: 3246403956,
num_txes: 20,
orphan_status: false,
pow_hash: HexVec::new(),
pow_hash: String::new(),
prev_hash: Hex(hex!(
"0ef6e948f77b8f8806621003f5de24b1bcbea150bc0e376835aea099674a5db5"
)),
@ -1263,7 +1263,7 @@ mod test {
nonce: 3737164176,
num_txes: 1,
orphan_status: false,
pow_hash: HexVec::new(),
pow_hash: String::new(),
prev_hash: Hex(hex!(
"86d1d20a40cefcf3dd410ff6967e0491613b77bf73ea8f1bf2e335cf9cf7d57a"
)),
@ -1283,7 +1283,7 @@ mod test {
json::GET_BLOCK_REQUEST,
GetBlockRequest {
height: 2751506,
hash: HexVec::new(),
hash: String::new(),
fill_pow_hash: false,
},
);
@ -1293,7 +1293,7 @@ mod test {
fn get_block_response() {
test_json_response(json::GET_BLOCK_RESPONSE, GetBlockResponse {
base: AccessResponseBase::OK,
blob: HexVec(hex!("1010c58bab9b06b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7807e07f502cef8a70101ff92f8a7010180e0a596bb1103d7cbf826b665d7a532c316982dc8dbc24f285cbc18bbcc27c7164cd9b3277a85d034019f629d8b36bd16a2bfce3ea80c31dc4d8762c67165aec21845494e32b7582fe00211000000297a787a000000000000000000000000").into()),
blob: "1010c58bab9b06b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7807e07f502cef8a70101ff92f8a7010180e0a596bb1103d7cbf826b665d7a532c316982dc8dbc24f285cbc18bbcc27c7164cd9b3277a85d034019f629d8b36bd16a2bfce3ea80c31dc4d8762c67165aec21845494e32b7582fe00211000000297a787a000000000000000000000000".into(),
block_header: BlockHeader {
block_size: 106,
block_weight: 106,
@ -1311,7 +1311,7 @@ mod test {
nonce: 4110909056,
num_txes: 0,
orphan_status: false,
pow_hash: HexVec::new(),
pow_hash: String::new(),
prev_hash: Hex(hex!("b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7")),
reward: 600000000000,
timestamp: 1667941829,
@ -1461,7 +1461,6 @@ mod test {
json::HARD_FORK_INFO_RESPONSE,
HardForkInfoResponse {
base: AccessResponseBase::OK,
hard_fork_info: HardForkInfo {
earliest_height: 2689608,
enabled: true,
state: 0,
@ -1471,7 +1470,6 @@ mod test {
voting: 16,
window: 10080,
},
},
);
}
@ -1906,7 +1904,7 @@ mod test {
test_json_request(json::CALC_POW_REQUEST, CalcPowRequest {
major_version: 14,
height: 2286447,
block_blob: HexVec(hex!("0e0ed286da8006ecdc1aab3033cf1716c52f13f9d8ae0051615a2453643de94643b550d543becd0000000002abc78b0101ffefc68b0101fcfcf0d4b422025014bb4a1eade6622fd781cb1063381cad396efa69719b41aa28b4fce8c7ad4b5f019ce1dc670456b24a5e03c2d9058a2df10fec779e2579753b1847b74ee644f16b023c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051399a1bc46a846474f5b33db24eae173a26393b976054ee14f9feefe99925233802867097564c9db7a36af5bb5ed33ab46e63092bd8d32cef121608c3258edd55562812e21cc7e3ac73045745a72f7d74581d9a0849d6f30e8b2923171253e864f4e9ddea3acb5bc755f1c4a878130a70c26297540bc0b7a57affb6b35c1f03d8dbd54ece8457531f8cba15bb74516779c01193e212050423020e45aa2c15dcb").into()),
block_blob: "0e0ed286da8006ecdc1aab3033cf1716c52f13f9d8ae0051615a2453643de94643b550d543becd0000000002abc78b0101ffefc68b0101fcfcf0d4b422025014bb4a1eade6622fd781cb1063381cad396efa69719b41aa28b4fce8c7ad4b5f019ce1dc670456b24a5e03c2d9058a2df10fec779e2579753b1847b74ee644f16b023c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051399a1bc46a846474f5b33db24eae173a26393b976054ee14f9feefe99925233802867097564c9db7a36af5bb5ed33ab46e63092bd8d32cef121608c3258edd55562812e21cc7e3ac73045745a72f7d74581d9a0849d6f30e8b2923171253e864f4e9ddea3acb5bc755f1c4a878130a70c26297540bc0b7a57affb6b35c1f03d8dbd54ece8457531f8cba15bb74516779c01193e212050423020e45aa2c15dcb".into(),
seed_hash: Hex(hex!("d432f499205150873b2572b5f033c9c6e4b7c6f3394bd2dd93822cd7085e7307")),
});
}
@ -1947,7 +1945,7 @@ mod test {
#[test]
fn add_aux_pow_request() {
test_json_request(json::ADD_AUX_POW_REQUEST, AddAuxPowRequest {
blocktemplate_blob: HexVec(hex!("1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()),
blocktemplate_blob: "1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(),
aux_pow: vec![AuxPow {
id: Hex(hex!("3200b4ea97c3b2081cd4190b58e49572b2319fed00d030ad51809dff06b5d8c8")),
hash: Hex(hex!("7b35762de164b20885e15dbe656b1138db06bb402fa1796f5765a23933d8859a"))
@ -1963,8 +1961,8 @@ mod test {
hash: Hex(hex!("7b35762de164b20885e15dbe656b1138db06bb402fa1796f5765a23933d8859a")),
id: Hex(hex!("3200b4ea97c3b2081cd4190b58e49572b2319fed00d030ad51809dff06b5d8c8")),
}],
blockhashing_blob: HexVec(hex!("1010ee97e2a106e9f8ebe8887e5b609949ac8ea6143e560ed13552b110cb009b21f0cfca1eaccf00000000b2685c1283a646bc9020c758daa443be145b7370ce5a6efacb3e614117032e2c22").into()),
blocktemplate_blob: HexVec(hex!("1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()),
blockhashing_blob: "1010ee97e2a106e9f8ebe8887e5b609949ac8ea6143e560ed13552b110cb009b21f0cfca1eaccf00000000b2685c1283a646bc9020c758daa443be145b7370ce5a6efacb3e614117032e2c22".into(),
blocktemplate_blob: "1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(),
merkle_root: Hex(hex!("7b35762de164b20885e15dbe656b1138db06bb402fa1796f5765a23933d8859a")),
merkle_tree_depth: 0,
});

View file

@ -33,9 +33,9 @@ use crate::constants::{
/// use cuprate_rpc_types::{
/// misc::Status,
/// CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
/// CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_FAILED
/// CORE_RPC_STATUS_PAYMENT_REQUIRED
/// };
/// use serde_json::{to_string, from_str};
/// use serde_json::to_string;
///
/// let other = Status::Other("OTHER".into());
///
@ -46,16 +46,6 @@ use crate::constants::{
/// assert_eq!(to_string(&Status::PaymentRequired).unwrap(), r#""PAYMENT REQUIRED""#);
/// assert_eq!(to_string(&other).unwrap(), r#""OTHER""#);
///
/// assert_eq!(from_str::<Status>(r#""Ok""#).unwrap(), Status::Ok);
/// assert_eq!(from_str::<Status>(r#""OK""#).unwrap(), Status::Ok);
/// assert_eq!(from_str::<Status>(r#""Failed""#).unwrap(), Status::Failed);
/// assert_eq!(from_str::<Status>(r#""FAILED""#).unwrap(), Status::Failed);
/// assert_eq!(from_str::<Status>(r#""Busy""#).unwrap(), Status::Busy);
/// assert_eq!(from_str::<Status>(r#""BUSY""#).unwrap(), Status::Busy);
/// assert_eq!(from_str::<Status>(r#""NOT MINING""#).unwrap(), Status::NotMining);
/// assert_eq!(from_str::<Status>(r#""PAYMENT REQUIRED""#).unwrap(), Status::PaymentRequired);
/// assert_eq!(from_str::<Status>(r#""OTHER""#).unwrap(), other);
///
/// assert_eq!(Status::Ok.as_ref(), CORE_RPC_STATUS_OK);
/// assert_eq!(Status::Failed.as_ref(), CORE_RPC_STATUS_FAILED);
/// assert_eq!(Status::Busy.as_ref(), CORE_RPC_STATUS_BUSY);
@ -84,16 +74,16 @@ pub enum Status {
// `#[serde(rename = "")]` only takes raw string literals?
// We have to re-type the constants here...
/// Successful RPC response, everything is OK; [`CORE_RPC_STATUS_OK`].
#[cfg_attr(feature = "serde", serde(rename = "OK", alias = "Ok"))]
#[cfg_attr(feature = "serde", serde(rename = "OK"))]
#[default]
Ok,
/// Generic request failure.
#[cfg_attr(feature = "serde", serde(alias = "FAILED"))]
#[cfg_attr(feature = "serde", serde(rename = "Failed"))]
Failed,
/// The daemon is busy, try later; [`CORE_RPC_STATUS_BUSY`].
#[cfg_attr(feature = "serde", serde(rename = "BUSY", alias = "Busy"))]
#[cfg_attr(feature = "serde", serde(rename = "BUSY"))]
Busy,
/// The daemon is not mining; [`CORE_RPC_STATUS_NOT_MINING`].

View file

@ -11,7 +11,7 @@ use cuprate_epee_encoding::{
EpeeObject, EpeeObjectBuilder,
};
use cuprate_hex::{Hex, HexVec};
use cuprate_hex::Hex;
#[cfg(feature = "serde")]
use crate::serde::{serde_false, serde_true};
@ -37,12 +37,12 @@ pub struct TxEntry {
/// to create this JSON string in a type-safe manner.
pub as_json: String,
pub as_hex: HexVec,
pub as_hex: String,
pub double_spend_seen: bool,
pub tx_hash: Hex<32>,
pub prunable_as_hex: HexVec,
pub prunable_as_hex: String,
pub prunable_hash: Hex<32>,
pub pruned_as_hex: HexVec,
pub pruned_as_hex: String,
#[cfg_attr(feature = "serde", serde(flatten))]
pub tx_entry_type: TxEntryType,

View file

@ -5,7 +5,7 @@
//! the [`crate::misc::ConnectionInfo`] struct defined here.
//---------------------------------------------------------------------------------------------------- Import
use cuprate_hex::{Hex, HexVec};
use cuprate_hex::Hex;
use cuprate_types::HardFork;
#[cfg(any(feature = "epee", feature = "serde"))]
@ -92,8 +92,8 @@ define_struct_and_impl_epee! {
nonce: u32,
num_txes: u64,
orphan_status: bool,
/// This is a [`Hex<32>`] that is sometimes empty.
pow_hash: HexVec,
/// This is an empty string if the `fill_pow_hash` param is `false`.
pow_hash: String,
prev_hash: Hex<32>,
reward: u64,
timestamp: u64,
@ -264,8 +264,8 @@ define_struct_and_impl_epee! {
mask: Hex<32>,
unlocked: bool,
height: u64,
/// This is a [`Hex<32>`] that is sometimes empty.
txid: HexVec,
/// Optionally empty with `/get_outs`'s `"get_txid": false`.
txid: String,
}
}
@ -307,7 +307,7 @@ define_struct_and_impl_epee! {
max_used_block_id_hash: Hex<32>,
receive_time: u64,
relayed: bool,
tx_blob: HexVec,
tx_blob: String,
tx_json: cuprate_types::json::tx::Transaction,
weight: u64 = default::<u64>(),
}

File diff suppressed because one or more lines are too long

View file

@ -2,7 +2,7 @@
//---------------------------------------------------------------------------------------------------- Import
use curve25519_dalek::edwards::CompressedEdwardsY;
use monero_serai::transaction::{Timelock, Transaction};
use monero_serai::transaction::Timelock;
use cuprate_database::{
DbResult, RuntimeError, {DatabaseRo, DatabaseRw},
@ -13,9 +13,7 @@ use cuprate_types::OutputOnChain;
use crate::{
ops::macros::{doc_add_block_inner_invariant, doc_error},
tables::{
BlockInfos, BlockTxsHashes, Outputs, RctOutputs, Tables, TablesMut, TxBlobs, TxUnlockTime,
},
tables::{BlockTxsHashes, Outputs, RctOutputs, Tables, TablesMut, TxUnlockTime},
types::{Amount, AmountIndex, Output, OutputFlags, PreRctOutputId, RctOutput},
};
@ -156,8 +154,6 @@ pub fn output_to_output_on_chain(
amount: Amount,
table_tx_unlock_time: &impl DatabaseRo<TxUnlockTime>,
table_block_txs_hashes: &impl DatabaseRo<BlockTxsHashes>,
table_block_infos: &impl DatabaseRo<BlockInfos>,
table_tx_blobs: &impl DatabaseRo<TxBlobs>,
) -> DbResult<OutputOnChain> {
let commitment = compute_zero_commitment(amount);
@ -177,13 +173,7 @@ pub fn output_to_output_on_chain(
let txid = {
let height = u32_to_usize(output.height);
let tx_idx = u64_to_usize(output.tx_idx);
if let Some(hash) = table_block_txs_hashes.get(&height)?.get(tx_idx) {
*hash
} else {
let miner_tx_id = table_block_infos.get(&height)?.mining_tx_index;
let tx_blob = table_tx_blobs.get(&miner_tx_id)?;
Transaction::read(&mut tx_blob.0.as_slice())?.hash()
}
table_block_txs_hashes.get(&height)?[tx_idx]
};
Ok(OutputOnChain {
@ -208,8 +198,6 @@ pub fn rct_output_to_output_on_chain(
rct_output: &RctOutput,
table_tx_unlock_time: &impl DatabaseRo<TxUnlockTime>,
table_block_txs_hashes: &impl DatabaseRo<BlockTxsHashes>,
table_block_infos: &impl DatabaseRo<BlockInfos>,
table_tx_blobs: &impl DatabaseRo<TxBlobs>,
) -> DbResult<OutputOnChain> {
// INVARIANT: Commitments stored are valid when stored by the database.
let commitment = CompressedEdwardsY::from_slice(&rct_output.commitment)
@ -233,13 +221,7 @@ pub fn rct_output_to_output_on_chain(
let txid = {
let height = u32_to_usize(rct_output.height);
let tx_idx = u64_to_usize(rct_output.tx_idx);
if let Some(hash) = table_block_txs_hashes.get(&height)?.get(tx_idx) {
*hash
} else {
let miner_tx_id = table_block_infos.get(&height)?.mining_tx_index;
let tx_blob = table_tx_blobs.get(&miner_tx_id)?;
Transaction::read(&mut tx_blob.0.as_slice())?.hash()
}
table_block_txs_hashes.get(&height)?[tx_idx]
};
Ok(OutputOnChain {
@ -263,8 +245,6 @@ pub fn id_to_output_on_chain(id: &PreRctOutputId, tables: &impl Tables) -> DbRes
&rct_output,
tables.tx_unlock_time(),
tables.block_txs_hashes(),
tables.block_infos(),
tables.tx_blobs(),
)?;
Ok(output_on_chain)
@ -276,8 +256,6 @@ pub fn id_to_output_on_chain(id: &PreRctOutputId, tables: &impl Tables) -> DbRes
id.amount,
tables.tx_unlock_time(),
tables.block_txs_hashes(),
tables.block_infos(),
tables.tx_blobs(),
)?;
Ok(output_on_chain)

View file

@ -1,2 +1,7 @@
# `cuprate-hex`
Cuprate's hexadecimal data types.
# Features flags
| Feature flag | Does what |
|--------------|-----------|
| `serde` | Enables `serde` on types where applicable

View file

@ -2,8 +2,6 @@
// Allow some lints when running in debug mode.
#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))]
mod array;
mod vec;
mod hex;
pub use array::Hex;
pub use vec::HexVec;
pub use hex::Hex;

1
types/hex/src/prefix.rs Normal file
View file

@ -0,0 +1 @@

View file

@ -1,131 +0,0 @@
//! Hexadecimal serde wrappers for [`Vec<u8>`].
//!
//! This module provides transparent wrapper types for
//! arrays that (de)serialize from hexadecimal input/output.
use hex::FromHexError;
use serde::{Deserialize, Deserializer, Serialize};
/// Wrapper type for a byte [`Vec`] that (de)serializes from/to hexadecimal strings.
///
/// ```rust
/// # use cuprate_hex::HexVec;
/// let hash = [1; 32];
/// let hex_vec = HexVec::from(hash);
/// let expected_json = r#""0101010101010101010101010101010101010101010101010101010101010101""#;
///
/// let to_string = serde_json::to_string(&hex_vec).unwrap();
/// assert_eq!(to_string, expected_json);
///
/// let from_str = serde_json::from_str::<HexVec>(expected_json).unwrap();
/// assert_eq!(hex_vec, from_str);
///
/// //------
///
/// let vec = vec![hex_vec; 2];
/// let expected_json = r#"["0101010101010101010101010101010101010101010101010101010101010101","0101010101010101010101010101010101010101010101010101010101010101"]"#;
///
/// let to_string = serde_json::to_string(&vec).unwrap();
/// assert_eq!(to_string, expected_json);
///
/// let from_str = serde_json::from_str::<Vec<HexVec>>(expected_json).unwrap();
/// assert_eq!(vec, from_str);
/// ```
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[serde(transparent)]
#[repr(transparent)]
pub struct HexVec(#[serde(with = "hex::serde")] pub Vec<u8>);
impl HexVec {
/// [`Vec::new`].
pub const fn new() -> Self {
Self(Vec::new())
}
/// [`Vec::is_empty`].
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Returns an empty [`Self`] if `array` is all `0`s.
///
/// ```rust
/// # use cuprate_hex::HexVec;
/// assert_eq!(HexVec::empty_if_zeroed([1; 32]).0, [1; 32]);
/// assert_eq!(HexVec::empty_if_zeroed([0; 32]), HexVec(vec![]));
/// assert!(HexVec::empty_if_zeroed([0; 32]).is_empty());
/// ```
pub fn empty_if_zeroed<const N: usize>(array: [u8; N]) -> Self {
if array == [0; N] {
Self(Vec::new())
} else {
Self(array.to_vec())
}
}
}
impl<'de> Deserialize<'de> for HexVec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self(hex::serde::deserialize(deserializer)?))
}
}
impl From<HexVec> for Vec<u8> {
fn from(hex: HexVec) -> Self {
hex.0
}
}
impl From<Vec<u8>> for HexVec {
fn from(value: Vec<u8>) -> Self {
Self(value)
}
}
impl<const N: usize> From<[u8; N]> for HexVec {
fn from(value: [u8; N]) -> Self {
Self(value.to_vec())
}
}
impl TryFrom<String> for HexVec {
type Error = FromHexError;
fn try_from(value: String) -> Result<Self, Self::Error> {
hex::decode(value).map(Self)
}
}
impl TryFrom<&str> for HexVec {
type Error = FromHexError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
hex::decode(value).map(Self)
}
}
impl<const N: usize> TryFrom<HexVec> for [u8; N] {
type Error = FromHexError;
fn try_from(value: HexVec) -> Result<Self, Self::Error> {
Self::try_from(value.0).map_err(|_| FromHexError::InvalidStringLength)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn asdf() {
let hash = vec![0; 32];
let hex_vec = HexVec(hash);
let expected_json = r#""0000000000000000000000000000000000000000000000000000000000000000""#;
let to_string = serde_json::to_string(&hex_vec).unwrap();
assert_eq!(to_string, expected_json);
let from_str = serde_json::from_str::<HexVec>(expected_json).unwrap();
assert_eq!(hex_vec, from_str);
}
}