Compare commits

..

5 commits

Author SHA1 Message Date
hinto.janai
b2b7e93e4c
json-rpc fixes
Some checks are pending
Deny / audit (push) Waiting to run
2024-12-19 20:41:50 -05:00
hinto.janai
114a5db52e
fix conflict 2024-12-19 18:42:17 -05:00
hinto.janai
d47f8ec7dc
fix doc tests 2024-12-19 18:38:08 -05:00
hinto.janai
dff77e8bed
fix get_txid for /get_outs
miner transaction was not accounted for
2024-12-19 18:11:00 -05:00
hinto.janai
cb6fdb76e4
HexVec 2024-12-19 17:22:35 -05:00
17 changed files with 909 additions and 538 deletions

860
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;
use cuprate_hex::{Hex, HexVec};
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, HardFork,
BlockTemplate, Chain, HardFork,
};
use crate::{
@ -162,7 +162,7 @@ async fn get_block_template(
}
let blob_reserve = hex::decode(request.extra_nonce)?;
let prev_block = helper::hex_to_hash(request.prev_block)?;
let prev_block: [u8; 32] = request.prev_block.try_into()?;
let extra_nonce = hex::decode(request.extra_nonce)?;
let BlockTemplate {
@ -182,11 +182,11 @@ async fn get_block_template(
)
.await?;
let blockhashing_blob = hex::encode(block.serialize_pow_hash());
let blocktemplate_blob = hex::encode(block.serialize());
let blockhashing_blob = HexVec(block.serialize_pow_hash());
let blocktemplate_blob = HexVec(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 = hex::encode(next_seed_hash);
let next_seed_hash = HexVec::empty_if_zeroed(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,7 +215,10 @@ async fn get_block_count(
) -> Result<GetBlockCountResponse, Error> {
Ok(GetBlockCountResponse {
base: helper::response_base(false),
count: helper::top_height(&mut state).await?.0,
// Block count starts at 1
count: blockchain::chain_height(&mut state.blockchain_read)
.await?
.0,
})
}
@ -225,12 +228,7 @@ 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,
todo!("access to `cuprated`'s Chain"),
)
.await?;
let hash = blockchain::block_hash(&mut state.blockchain_read, height, Chain::Main).await?;
Ok(OnGetBlockHashResponse {
block_hash: Hex(hash),
@ -244,8 +242,7 @@ async fn submit_block(
) -> Result<SubmitBlockResponse, Error> {
// Parse hex into block.
let [blob] = request.block_blob;
let bytes = hex::decode(blob)?;
let block = Block::read(&mut bytes.as_slice())?;
let block = Block::read(&mut blob.0.as_slice())?;
let block_id = Hex(block.hash());
// Attempt to relay the block.
@ -272,7 +269,7 @@ async fn generate_blocks(
let prev_block = if request.prev_block.is_empty() {
None
} else {
Some(helper::hex_to_hash(request.prev_block)?)
Some(request.prev_block.try_into()?)
};
let (blocks, height) = blockchain_manager::generate_blocks(
@ -318,21 +315,13 @@ async fn get_block_header_by_hash(
));
}
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?;
let block_header =
helper::block_header_by_hash(&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 = get(&mut state, hash.0, request.fill_pow_hash).await?;
let hash = helper::block_header_by_hash(&mut state, hash.0, request.fill_pow_hash).await?;
block_headers.push(hash);
}
@ -425,16 +414,16 @@ async fn get_block(
helper::block_header(&mut state, request.height, request.fill_pow_hash).await?;
(block, block_header)
} else {
let hash = helper::hex_to_hash(request.hash)?;
let hash: [u8; 32] = request.hash.try_into()?;
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 = hex::encode(block.serialize());
let blob = HexVec(block.serialize());
let miner_tx_hash = Hex(block.miner_transaction.hash());
let tx_hashes = block.transactions.iter().map(|a| Hex(*a)).collect();
let tx_hashes = block.transactions.iter().copied().map(Hex).collect();
let json = {
let block = cuprate_types::json::block::Block::from(block);
serde_json::to_string_pretty(&block)?
@ -473,8 +462,7 @@ 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(); // TODO: is this correct?
let adjusted_time = c.current_adjusted_timestamp_for_time_lock();
let c = &c.context_to_verify_block;
let alt_blocks_count = if restricted {
@ -482,76 +470,98 @@ 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); // TODO: is this correct?
let block_weight_median = usize_to_u64(c.median_weight_for_block_reward); // TODO: is this correct?
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_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 {
todo!("support bootstrap daemon")
(String::new(), false)
};
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?
};
let (mainnet, testnet, stagenet) = match todo!("access to `cuprated`'s `Network`") {
// TODO: This should be `cuprated`'s active network.
let network = Network::Mainnet;
let (mainnet, testnet, stagenet) = match network {
Network::Mainnet => (true, false, false),
Network::Testnet => (false, true, false),
Network::Stagenet => (false, false, true),
};
// 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 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 };
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?;
let update_available = if restricted {
false
} else {
todo!("implement an update checker for `cuprated`")
};
#[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 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);
@ -613,18 +623,12 @@ async fn hard_fork_info(
.current_hf
};
let info = blockchain_context::hard_fork_info(&mut state.blockchain_context, hard_fork).await?;
let hard_fork_info =
blockchain_context::hard_fork_info(&mut state.blockchain_context, hard_fork).await?;
Ok(HardForkInfoResponse {
base: helper::access_response_base(false),
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,
hard_fork_info,
})
}
@ -1024,8 +1028,7 @@ async fn calc_pow(
request: CalcPowRequest,
) -> Result<CalcPowResponse, Error> {
let hardfork = HardFork::from_version(request.major_version)?;
let block_blob: Vec<u8> = hex::decode(request.block_blob)?;
let block = Block::read(&mut block_blob.as_slice())?;
let block = Block::read(&mut request.block_blob.0.as_slice())?;
let seed_hash = request.seed_hash.0;
// let block_weight = todo!();
@ -1043,6 +1046,7 @@ async fn calc_pow(
// }
// TODO: will `CalculatePow` do the above checks?
let pow_hash = blockchain_context::calculate_pow(
&mut state.blockchain_context,
hardfork,
@ -1056,23 +1060,16 @@ 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?
}
@ -1222,10 +1219,7 @@ 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 = {
let hex = hex::decode(request.blocktemplate_blob)?;
Block::read(&mut hex.as_slice())?
};
let block_template = Block::read(&mut request.blocktemplate_blob.0.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")
@ -1256,8 +1250,8 @@ fn add_aux_pow_inner(
let blocktemplate_blob = block_template.serialize();
let blockhashing_blob = block_template.serialize_pow_hash();
let blocktemplate_blob = hex::encode(blocktemplate_blob);
let blockhashing_blob = hex::encode(blockhashing_blob);
let blocktemplate_blob = HexVec(blocktemplate_blob);
let blockhashing_blob = HexVec(blockhashing_blob);
let merkle_root = Hex(merkle_root);
let aux_pow = aux_pow.into_vec();
@ -1271,16 +1265,25 @@ 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!(),
txids: todo!("this RPC call is not yet in the v0.18 branch."),
})
}
//---------------------------------------------------------------------------------------------------- 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;
use cuprate_hex::{Hex, HexVec};
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() {
(String::new(), String::new())
(HexVec::new(), HexVec::new())
} else {
(hex::encode(tx.pruned_blob), hex::encode(tx.prunable_blob))
(HexVec(tx.pruned_blob), HexVec(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'
String::new()
HexVec::new()
} else {
hex::encode(&tx.tx_blob)
HexVec(tx.tx_blob)
};
txs_as_hex.push(as_hex.clone());
let as_json = if request.decode_as_json {
let tx = Transaction::read(&mut tx.tx_blob.as_slice())?;
let tx = Transaction::read(&mut as_hex.0.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 = String::new();
let prunable_as_hex = String::new();
let pruned_as_hex = HexVec::new();
let prunable_as_hex = HexVec::new();
let prunable_hash = Hex([0; 32]);
let as_hex = hex::encode(tx_blob);
let as_hex = HexVec(tx_blob);
txs_as_hex.push(as_hex.clone());
let as_json = if request.decode_as_json {
@ -351,10 +351,7 @@ async fn send_raw_transaction(
tx_extra_too_big: false,
};
let tx = {
let blob = hex::decode(request.tx_as_hex)?;
Transaction::read(&mut blob.as_slice())?
};
let tx = Transaction::read(&mut request.tx_as_hex.0.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))
}
/// TODO
/// [`TxpoolReadRequest::PoolInfo`]
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)
}
/// TODO
/// [`TxpoolReadRequest::TxsByHash`]
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)
}
/// TODO
/// [`TxpoolReadRequest::KeyImagesSpent`]
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)
}
/// TODO
/// [`TxpoolReadRequest::Pool`]
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))
}
/// TODO
/// [`TxpoolReadRequest::PoolStats`]
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)
}
/// TODO
/// [`TxpoolReadRequest::AllHashes`]
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
/// TODO: impl txpool manager.
pub async fn flush(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> {
todo!();
Ok(())
}
/// TODO
/// TODO: impl txpool manager.
pub async fn relay(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> {
todo!();
Ok(())
}
/// TODO
/// TODO: impl txpool manager.
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;
use cuprate_hex::{Hex, HexVec};
use crate::{
io::{checked_read_primitive, checked_write_primitive},
@ -437,6 +437,18 @@ 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> {
@ -503,6 +515,7 @@ 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;
use cuprate_hex::{Hex, HexVec};
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(String::new, hex::encode),
pow_hash: x.pow_hash.map_or_else(HexVec::new, |a| HexVec(a.into())),
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: hex::encode(x.tx_blob),
tx_blob: HexVec(x.tx_blob),
tx_json: x.tx_json,
weight: x.weight,
}
@ -201,7 +201,11 @@ impl From<crate::misc::OutKeyBin> for crate::misc::OutKey {
mask: Hex(x.mask),
unlocked: x.unlocked,
height: x.height,
txid: hex::encode(x.txid),
txid: if x.txid == [0; 32] {
HexVec::new()
} else {
HexVec::from(x.txid)
},
}
}
}

View file

@ -6,8 +6,10 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use cuprate_hex::Hex;
use cuprate_types::rpc::{AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry};
use cuprate_hex::{Hex, HexVec};
use cuprate_types::rpc::{
AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, HardForkInfo, TxBacklogEntry,
};
use crate::{
base::{AccessResponseBase, ResponseBase},
@ -79,7 +81,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: String = default::<String>(), "default",
prev_block: HexVec = default::<HexVec>(), "default",
// Another optional expression:
// This indicates to the macro to (de)serialize
@ -116,13 +118,14 @@ define_request_and_response! {
// status: crate::Status,
// untrusted: bool,
// ```
blockhashing_blob: String,
blocktemplate_blob: String,
blockhashing_blob: HexVec,
blocktemplate_blob: HexVec,
difficulty_top64: u64,
difficulty: u64,
expected_reward: u64,
height: u64,
next_seed_hash: String,
/// This is a [`Hex<32>`] that is sometimes empty.
next_seed_hash: HexVec,
prev_hash: Hex<32>,
reserved_offset: u64,
seed_hash: Hex<32>,
@ -179,7 +182,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: [String; 1],
block_blob: [HexVec; 1],
},
// FIXME: `cuprate_test_utils` only has an `error` response for this.
@ -197,7 +200,7 @@ define_request_and_response! {
Request {
amount_of_blocks: u64,
prev_block: String = default::<String>(), "default",
prev_block: HexVec = default::<HexVec>(), "default",
starting_nonce: u32,
wallet_address: String,
},
@ -290,13 +293,15 @@ 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>
hash: String = default::<String>(), "default",
/// This is a [`Hex<32>`] that is sometimes empty.
hash: HexVec = default::<HexVec>(), "default",
height: u64 = default::<u64>(), "default",
fill_pow_hash: bool = default::<bool>(), "default",
},
AccessResponseBase {
blob: String,
blob: HexVec,
block_header: BlockHeader,
/// `cuprate_types::json::block::Block` should be used
/// to create this JSON string in a type-safe manner.
@ -382,14 +387,9 @@ define_request_and_response! {
},
AccessResponseBase {
earliest_height: u64,
enabled: bool,
state: u32,
threshold: u32,
version: u8,
votes: u32,
voting: u8,
window: u32,
/// This field is [flattened](https://serde.rs/field-attrs.html#flatten).
#[cfg_attr(feature = "serde", serde(flatten))]
hard_fork_info: HardForkInfo,
}
}
@ -662,7 +662,7 @@ define_request_and_response! {
Request {
major_version: u8,
height: u64,
block_blob: String,
block_blob: HexVec,
seed_hash: Hex<32>,
},
@ -697,13 +697,13 @@ define_request_and_response! {
AddAuxPow,
Request {
blocktemplate_blob: String,
blocktemplate_blob: HexVec,
aux_pow: Vec<AuxPow>,
},
ResponseBase {
blocktemplate_blob: String,
blockhashing_blob: String,
blocktemplate_blob: HexVec,
blockhashing_blob: HexVec,
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<String>,
txids: Vec<Hex<32>>,
}
}
@ -959,7 +959,7 @@ mod test {
test_json_request(json::GET_BLOCK_TEMPLATE_REQUEST, GetBlockTemplateRequest {
reserve_size: 60,
extra_nonce: String::default(),
prev_block: String::default(),
prev_block: HexVec::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: "1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a00000000e0c20372be23d356347091025c5b5e8f2abf83ab618378565cce2b703491523401".into(),
blocktemplate_blob: "1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(),
blockhashing_blob: HexVec(hex!("1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a00000000e0c20372be23d356347091025c5b5e8f2abf83ab618378565cce2b703491523401").into()),
blocktemplate_blob: HexVec(hex!("1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()),
difficulty_top64: 0,
difficulty: 283305047039,
expected_reward: 600000000000,
height: 3195018,
next_seed_hash: String::new(),
next_seed_hash: HexVec::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: ["0707e6bdfedc053771512f1bc27c62731ae9e8f2443db64ce742f4e57f5cf8d393de28551e441a0000000002fb830a01ffbf830a018cfe88bee283060274c0aae2ef5730e680308d9c00b6da59187ad0352efe3c71d36eeeb28782f29f2501bd56b952c3ddc3e350c2631d3a5086cac172c56893831228b17de296ff4669de020200000000".into()],
block_blob: [HexVec(hex!("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: String::default(),
prev_block: HexVec::default(),
wallet_address: "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A".into(),
starting_nonce: 0
});
@ -1074,7 +1074,7 @@ mod test {
nonce: 1885649739,
num_txes: 37,
orphan_status: false,
pow_hash: String::new(),
pow_hash: HexVec::new(),
prev_hash: Hex(hex!(
"22c72248ae9c5a2863c94735d710a3525c499f70707d1c2f395169bc5c8a0da3"
)),
@ -1129,7 +1129,7 @@ mod test {
nonce: 1646,
num_txes: 0,
orphan_status: false,
pow_hash: String::new(),
pow_hash: HexVec::new(),
prev_hash: Hex(hex!(
"b61c58b2e0be53fad5ef9d9731a55e8a81d972b8d90ed07c04fd37ca6403ff78"
)),
@ -1180,7 +1180,7 @@ mod test {
nonce: 1646,
num_txes: 0,
orphan_status: false,
pow_hash: String::new(),
pow_hash: HexVec::new(),
prev_hash: Hex(hex!(
"b61c58b2e0be53fad5ef9d9731a55e8a81d972b8d90ed07c04fd37ca6403ff78"
)),
@ -1233,7 +1233,7 @@ mod test {
nonce: 3246403956,
num_txes: 20,
orphan_status: false,
pow_hash: String::new(),
pow_hash: HexVec::new(),
prev_hash: Hex(hex!(
"0ef6e948f77b8f8806621003f5de24b1bcbea150bc0e376835aea099674a5db5"
)),
@ -1263,7 +1263,7 @@ mod test {
nonce: 3737164176,
num_txes: 1,
orphan_status: false,
pow_hash: String::new(),
pow_hash: HexVec::new(),
prev_hash: Hex(hex!(
"86d1d20a40cefcf3dd410ff6967e0491613b77bf73ea8f1bf2e335cf9cf7d57a"
)),
@ -1283,7 +1283,7 @@ mod test {
json::GET_BLOCK_REQUEST,
GetBlockRequest {
height: 2751506,
hash: String::new(),
hash: HexVec::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: "1010c58bab9b06b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7807e07f502cef8a70101ff92f8a7010180e0a596bb1103d7cbf826b665d7a532c316982dc8dbc24f285cbc18bbcc27c7164cd9b3277a85d034019f629d8b36bd16a2bfce3ea80c31dc4d8762c67165aec21845494e32b7582fe00211000000297a787a000000000000000000000000".into(),
blob: HexVec(hex!("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: String::new(),
pow_hash: HexVec::new(),
prev_hash: Hex(hex!("b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7")),
reward: 600000000000,
timestamp: 1667941829,
@ -1461,14 +1461,16 @@ mod test {
json::HARD_FORK_INFO_RESPONSE,
HardForkInfoResponse {
base: AccessResponseBase::OK,
earliest_height: 2689608,
enabled: true,
state: 0,
threshold: 0,
version: 16,
votes: 10080,
voting: 16,
window: 10080,
hard_fork_info: HardForkInfo {
earliest_height: 2689608,
enabled: true,
state: 0,
threshold: 0,
version: 16,
votes: 10080,
voting: 16,
window: 10080,
},
},
);
}
@ -1904,7 +1906,7 @@ mod test {
test_json_request(json::CALC_POW_REQUEST, CalcPowRequest {
major_version: 14,
height: 2286447,
block_blob: "0e0ed286da8006ecdc1aab3033cf1716c52f13f9d8ae0051615a2453643de94643b550d543becd0000000002abc78b0101ffefc68b0101fcfcf0d4b422025014bb4a1eade6622fd781cb1063381cad396efa69719b41aa28b4fce8c7ad4b5f019ce1dc670456b24a5e03c2d9058a2df10fec779e2579753b1847b74ee644f16b023c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051399a1bc46a846474f5b33db24eae173a26393b976054ee14f9feefe99925233802867097564c9db7a36af5bb5ed33ab46e63092bd8d32cef121608c3258edd55562812e21cc7e3ac73045745a72f7d74581d9a0849d6f30e8b2923171253e864f4e9ddea3acb5bc755f1c4a878130a70c26297540bc0b7a57affb6b35c1f03d8dbd54ece8457531f8cba15bb74516779c01193e212050423020e45aa2c15dcb".into(),
block_blob: HexVec(hex!("0e0ed286da8006ecdc1aab3033cf1716c52f13f9d8ae0051615a2453643de94643b550d543becd0000000002abc78b0101ffefc68b0101fcfcf0d4b422025014bb4a1eade6622fd781cb1063381cad396efa69719b41aa28b4fce8c7ad4b5f019ce1dc670456b24a5e03c2d9058a2df10fec779e2579753b1847b74ee644f16b023c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051399a1bc46a846474f5b33db24eae173a26393b976054ee14f9feefe99925233802867097564c9db7a36af5bb5ed33ab46e63092bd8d32cef121608c3258edd55562812e21cc7e3ac73045745a72f7d74581d9a0849d6f30e8b2923171253e864f4e9ddea3acb5bc755f1c4a878130a70c26297540bc0b7a57affb6b35c1f03d8dbd54ece8457531f8cba15bb74516779c01193e212050423020e45aa2c15dcb").into()),
seed_hash: Hex(hex!("d432f499205150873b2572b5f033c9c6e4b7c6f3394bd2dd93822cd7085e7307")),
});
}
@ -1945,7 +1947,7 @@ mod test {
#[test]
fn add_aux_pow_request() {
test_json_request(json::ADD_AUX_POW_REQUEST, AddAuxPowRequest {
blocktemplate_blob: "1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(),
blocktemplate_blob: HexVec(hex!("1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()),
aux_pow: vec![AuxPow {
id: Hex(hex!("3200b4ea97c3b2081cd4190b58e49572b2319fed00d030ad51809dff06b5d8c8")),
hash: Hex(hex!("7b35762de164b20885e15dbe656b1138db06bb402fa1796f5765a23933d8859a"))
@ -1961,8 +1963,8 @@ mod test {
hash: Hex(hex!("7b35762de164b20885e15dbe656b1138db06bb402fa1796f5765a23933d8859a")),
id: Hex(hex!("3200b4ea97c3b2081cd4190b58e49572b2319fed00d030ad51809dff06b5d8c8")),
}],
blockhashing_blob: "1010ee97e2a106e9f8ebe8887e5b609949ac8ea6143e560ed13552b110cb009b21f0cfca1eaccf00000000b2685c1283a646bc9020c758daa443be145b7370ce5a6efacb3e614117032e2c22".into(),
blocktemplate_blob: "1010f4bae0b4069d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a0000000002c681c30101ff8a81c3010180e0a596bb11033b7eedf47baf878f3490cb20b696079c34bd017fe59b0d070e74d73ffabc4bb0e05f011decb630f3148d0163b3bd39690dde4078e4cfb69fecf020d6278a27bad10c58023c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(),
blockhashing_blob: HexVec(hex!("1010ee97e2a106e9f8ebe8887e5b609949ac8ea6143e560ed13552b110cb009b21f0cfca1eaccf00000000b2685c1283a646bc9020c758daa443be145b7370ce5a6efacb3e614117032e2c22").into()),
blocktemplate_blob: HexVec(hex!("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_PAYMENT_REQUIRED, CORE_RPC_STATUS_FAILED
/// };
/// use serde_json::to_string;
/// use serde_json::{to_string, from_str};
///
/// let other = Status::Other("OTHER".into());
///
@ -46,6 +46,16 @@ 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);
@ -74,16 +84,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"))]
#[cfg_attr(feature = "serde", serde(rename = "OK", alias = "Ok"))]
#[default]
Ok,
/// Generic request failure.
#[cfg_attr(feature = "serde", serde(rename = "Failed"))]
#[cfg_attr(feature = "serde", serde(alias = "FAILED"))]
Failed,
/// The daemon is busy, try later; [`CORE_RPC_STATUS_BUSY`].
#[cfg_attr(feature = "serde", serde(rename = "BUSY"))]
#[cfg_attr(feature = "serde", serde(rename = "BUSY", alias = "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;
use cuprate_hex::{Hex, HexVec};
#[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: String,
pub as_hex: HexVec,
pub double_spend_seen: bool,
pub tx_hash: Hex<32>,
pub prunable_as_hex: String,
pub prunable_as_hex: HexVec,
pub prunable_hash: Hex<32>,
pub pruned_as_hex: String,
pub pruned_as_hex: HexVec,
#[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;
use cuprate_hex::{Hex, HexVec};
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 an empty string if the `fill_pow_hash` param is `false`.
pow_hash: String,
/// This is a [`Hex<32>`] that is sometimes empty.
pow_hash: HexVec,
prev_hash: Hex<32>,
reward: u64,
timestamp: u64,
@ -264,8 +264,8 @@ define_struct_and_impl_epee! {
mask: Hex<32>,
unlocked: bool,
height: u64,
/// Optionally empty with `/get_outs`'s `"get_txid": false`.
txid: String,
/// This is a [`Hex<32>`] that is sometimes empty.
txid: HexVec,
}
}
@ -307,7 +307,7 @@ define_struct_and_impl_epee! {
max_used_block_id_hash: Hex<32>,
receive_time: u64,
relayed: bool,
tx_blob: String,
tx_blob: HexVec,
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;
use monero_serai::transaction::{Timelock, Transaction};
use cuprate_database::{
DbResult, RuntimeError, {DatabaseRo, DatabaseRw},
@ -13,7 +13,9 @@ use cuprate_types::OutputOnChain;
use crate::{
ops::macros::{doc_add_block_inner_invariant, doc_error},
tables::{BlockTxsHashes, Outputs, RctOutputs, Tables, TablesMut, TxUnlockTime},
tables::{
BlockInfos, BlockTxsHashes, Outputs, RctOutputs, Tables, TablesMut, TxBlobs, TxUnlockTime,
},
types::{Amount, AmountIndex, Output, OutputFlags, PreRctOutputId, RctOutput},
};
@ -154,6 +156,8 @@ 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);
@ -173,7 +177,13 @@ 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);
table_block_txs_hashes.get(&height)?[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()
}
};
Ok(OutputOnChain {
@ -198,6 +208,8 @@ 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)
@ -221,7 +233,13 @@ 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);
table_block_txs_hashes.get(&height)?[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()
}
};
Ok(OutputOnChain {
@ -245,6 +263,8 @@ 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)
@ -256,6 +276,8 @@ 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,7 +1,2 @@
# `cuprate-hex`
Cuprate's hexadecimal data types.
# Features flags
| Feature flag | Does what |
|--------------|-----------|
| `serde` | Enables `serde` on types where applicable
Cuprate's hexadecimal data types.

View file

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

View file

@ -1 +0,0 @@

131
types/hex/src/vec.rs Normal file
View file

@ -0,0 +1,131 @@
//! 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);
}
}