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, fmt::HexPrefix,
map::split_u128_into_low_high_bits, 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_p2p_core::{client::handshaker::builder::DummyAddressBook, ClearNet, Network};
use cuprate_rpc_interface::RpcHandler; use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{ use cuprate_rpc_types::{
@ -54,7 +54,7 @@ use cuprate_rpc_types::{
}; };
use cuprate_types::{ use cuprate_types::{
rpc::{AuxPow, CoinbaseTxSum, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry}, rpc::{AuxPow, CoinbaseTxSum, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry},
BlockTemplate, Chain, HardFork, BlockTemplate, HardFork,
}; };
use crate::{ use crate::{
@ -162,7 +162,7 @@ async fn get_block_template(
} }
let blob_reserve = hex::decode(request.extra_nonce)?; 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 extra_nonce = hex::decode(request.extra_nonce)?;
let BlockTemplate { let BlockTemplate {
@ -182,11 +182,11 @@ async fn get_block_template(
) )
.await?; .await?;
let blockhashing_blob = HexVec(block.serialize_pow_hash()); let blockhashing_blob = hex::encode(block.serialize_pow_hash());
let blocktemplate_blob = HexVec(block.serialize()); let blocktemplate_blob = hex::encode(block.serialize());
let (difficulty, difficulty_top64) = split_u128_into_low_high_bits(difficulty); let (difficulty, difficulty_top64) = split_u128_into_low_high_bits(difficulty);
// let next_seed_hash = Hex(next_seed_hash); // 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 prev_hash = Hex(block.header.previous);
let seed_hash = Hex(seed_hash); let seed_hash = Hex(seed_hash);
let wide_difficulty = (difficulty, difficulty_top64).hex_prefix(); let wide_difficulty = (difficulty, difficulty_top64).hex_prefix();
@ -215,10 +215,7 @@ async fn get_block_count(
) -> Result<GetBlockCountResponse, Error> { ) -> Result<GetBlockCountResponse, Error> {
Ok(GetBlockCountResponse { Ok(GetBlockCountResponse {
base: helper::response_base(false), base: helper::response_base(false),
// Block count starts at 1 count: helper::top_height(&mut state).await?.0,
count: blockchain::chain_height(&mut state.blockchain_read)
.await?
.0,
}) })
} }
@ -228,7 +225,12 @@ async fn on_get_block_hash(
request: OnGetBlockHashRequest, request: OnGetBlockHashRequest,
) -> Result<OnGetBlockHashResponse, Error> { ) -> Result<OnGetBlockHashResponse, Error> {
let [height] = request.block_height; 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 { Ok(OnGetBlockHashResponse {
block_hash: Hex(hash), block_hash: Hex(hash),
@ -242,7 +244,8 @@ async fn submit_block(
) -> Result<SubmitBlockResponse, Error> { ) -> Result<SubmitBlockResponse, Error> {
// Parse hex into block. // Parse hex into block.
let [blob] = request.block_blob; 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()); let block_id = Hex(block.hash());
// Attempt to relay the block. // Attempt to relay the block.
@ -269,7 +272,7 @@ async fn generate_blocks(
let prev_block = if request.prev_block.is_empty() { let prev_block = if request.prev_block.is_empty() {
None None
} else { } else {
Some(request.prev_block.try_into()?) Some(helper::hex_to_hash(request.prev_block)?)
}; };
let (blocks, height) = blockchain_manager::generate_blocks( let (blocks, height) = blockchain_manager::generate_blocks(
@ -315,13 +318,21 @@ async fn get_block_header_by_hash(
)); ));
} }
let block_header = async fn get(
helper::block_header_by_hash(&mut state, request.hash.0, request.fill_pow_hash).await?; 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. // 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()); let mut block_headers = Vec::with_capacity(request.hashes.len());
for hash in request.hashes { 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); block_headers.push(hash);
} }
@ -414,16 +425,16 @@ async fn get_block(
helper::block_header(&mut state, request.height, request.fill_pow_hash).await?; helper::block_header(&mut state, request.height, request.fill_pow_hash).await?;
(block, block_header) (block, block_header)
} else { } 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 = blockchain::block_by_hash(&mut state.blockchain_read, hash).await?;
let block_header = let block_header =
helper::block_header_by_hash(&mut state, hash, request.fill_pow_hash).await?; helper::block_header_by_hash(&mut state, hash, request.fill_pow_hash).await?;
(block, block_header) (block, block_header)
}; };
let blob = HexVec(block.serialize()); let blob = hex::encode(block.serialize());
let miner_tx_hash = Hex(block.miner_transaction.hash()); 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 json = {
let block = cuprate_types::json::block::Block::from(block); let block = cuprate_types::json::block::Block::from(block);
serde_json::to_string_pretty(&block)? serde_json::to_string_pretty(&block)?
@ -462,7 +473,8 @@ async fn get_info(
let c = context.unchecked_blockchain_context(); let c = context.unchecked_blockchain_context();
let cumulative_difficulty = c.cumulative_difficulty; 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 c = &c.context_to_verify_block;
let alt_blocks_count = if restricted { let alt_blocks_count = if restricted {
@ -470,98 +482,76 @@ async fn get_info(
} else { } else {
blockchain::alt_chain_count(&mut state.blockchain_read).await? blockchain::alt_chain_count(&mut state.blockchain_read).await?
}; };
let block_weight_limit = usize_to_u64(c.effective_median_weight); // TODO: 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); // TODO: is this correct?
let block_weight_median = usize_to_u64(c.median_weight_for_block_reward);
let block_size_limit = block_weight_limit; let block_size_limit = block_weight_limit;
let block_size_median = block_weight_median; let block_size_median = block_weight_median;
#[expect(clippy::if_same_then_else, reason = "TODO: support bootstrap")]
let (bootstrap_daemon_address, was_bootstrap_ever_used) = if restricted { let (bootstrap_daemon_address, was_bootstrap_ever_used) = if restricted {
(String::new(), false) (String::new(), false)
} else { } else {
(String::new(), false) todo!("support bootstrap daemon")
}; };
let busy_syncing = blockchain_manager::syncing(&mut state.blockchain_manager).await?; let busy_syncing = blockchain_manager::syncing(&mut state.blockchain_manager).await?;
let (cumulative_difficulty, cumulative_difficulty_top64) = let (cumulative_difficulty, cumulative_difficulty_top64) =
split_u128_into_low_high_bits(cumulative_difficulty); 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) = blockchain::database_size(&mut state.blockchain_read).await?;
let (database_size, free_space) = if restricted { let (database_size, free_space) = if restricted {
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L131-L134> // <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 { const fn round_up(value: u64, quantum: u64) -> u64 {
value.div_ceil(quantum) value.div_ceil(quantum)
} }
let database_size = round_up(database_size, 5 * 1024 * 1024 * 1024); let database_size = round_up(database_size, 5 * 1024 * 1024 * 1024);
(database_size, u64::MAX) (database_size, u64::MAX)
} else { } else {
(database_size, free_space) (database_size, free_space)
}; };
let (difficulty, difficulty_top64) = split_u128_into_low_high_bits(c.next_difficulty); let (difficulty, difficulty_top64) = split_u128_into_low_high_bits(c.next_difficulty);
let height = usize_to_u64(c.chain_height); let height = usize_to_u64(c.chain_height);
let height_without_bootstrap = if restricted { 0 } else { height }; let height_without_bootstrap = if restricted { 0 } else { height };
let (incoming_connections_count, outgoing_connections_count) = if restricted { let (incoming_connections_count, outgoing_connections_count) = if restricted {
(0, 0) (0, 0)
} else { } else {
address_book::connection_count::<ClearNet>(&mut DummyAddressBook).await? 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::Mainnet => (true, false, false),
Network::Testnet => (false, true, false), Network::Testnet => (false, true, false),
Network::Stagenet => (false, false, true), Network::Stagenet => (false, false, true),
}; };
// TODO: make sure this is:
let nettype = network.to_string(); // - the same case as `monerod`
// TODO: access to CLI/config's `--offline` // - untagged (no `Network::`)
let offline = false; let nettype = todo!("access to `cuprated`'s `Network`").to_string();
let offline = todo!("access to CLI/config's `--offline`");
#[expect( let rpc_connections_count = if restricted {
clippy::if_same_then_else, 0
reason = "TODO: implement a connection counter in axum/RPC" } else {
)] 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 start_time = if restricted { 0 } else { *START_INSTANT_UNIX };
let synchronized = blockchain_manager::synced(&mut state.blockchain_manager).await?; let synchronized = blockchain_manager::synced(&mut state.blockchain_manager).await?;
let target_height = blockchain_manager::target_height(&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) let target = blockchain_manager::target(&mut state.blockchain_manager)
.await? .await?
.as_secs(); .as_secs();
let top_block_hash = Hex(c.top_hash); let top_block_hash = Hex(c.top_hash);
let tx_count = blockchain::total_tx_count(&mut state.blockchain_read).await?; 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 tx_pool_size = txpool::size(&mut state.txpool_read, !restricted).await?;
let update_available = if restricted {
#[expect( false
clippy::if_same_then_else, } else {
clippy::needless_bool, todo!("implement an update checker for `cuprated`")
reason = "TODO: implement an update checker for `cuprated`?" };
)]
let update_available = if restricted { false } else { false };
let version = if restricted { let version = if restricted {
String::new() String::new()
} else { } else {
VERSION_BUILD.to_string() VERSION_BUILD.to_string()
}; };
let (white_peerlist_size, grey_peerlist_size) = if restricted { let (white_peerlist_size, grey_peerlist_size) = if restricted {
(0, 0) (0, 0)
} else { } else {
address_book::peerlist_size::<ClearNet>(&mut DummyAddressBook).await? address_book::peerlist_size::<ClearNet>(&mut DummyAddressBook).await?
}; };
let wide_cumulative_difficulty = format!("{cumulative_difficulty:#x}"); let wide_cumulative_difficulty = format!("{cumulative_difficulty:#x}");
let wide_difficulty = format!("{:#x}", c.next_difficulty); let wide_difficulty = format!("{:#x}", c.next_difficulty);
@ -623,12 +613,18 @@ async fn hard_fork_info(
.current_hf .current_hf
}; };
let hard_fork_info = let info = blockchain_context::hard_fork_info(&mut state.blockchain_context, hard_fork).await?;
blockchain_context::hard_fork_info(&mut state.blockchain_context, hard_fork).await?;
Ok(HardForkInfoResponse { Ok(HardForkInfoResponse {
base: helper::access_response_base(false), 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, request: CalcPowRequest,
) -> Result<CalcPowResponse, Error> { ) -> Result<CalcPowResponse, Error> {
let hardfork = HardFork::from_version(request.major_version)?; 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 seed_hash = request.seed_hash.0;
// let block_weight = todo!(); // let block_weight = todo!();
@ -1046,7 +1043,6 @@ async fn calc_pow(
// } // }
// TODO: will `CalculatePow` do the above checks? // TODO: will `CalculatePow` do the above checks?
let pow_hash = blockchain_context::calculate_pow( let pow_hash = blockchain_context::calculate_pow(
&mut state.blockchain_context, &mut state.blockchain_context,
hardfork, 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`]. /// An async-friendly wrapper for [`add_aux_pow_inner`].
async fn add_aux_pow( async fn add_aux_pow(
state: CupratedRpcHandler, state: CupratedRpcHandler,
request: AddAuxPowRequest, request: AddAuxPowRequest,
) -> Result<AddAuxPowResponse, Error> { ) -> 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? 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_root = tree_hash(aux_pow_raw.as_ref());
let merkle_tree_depth = encode_mm_depth(len, nonce); 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<(), ()> { fn remove_field_from_tx_extra() -> Result<(), ()> {
todo!("https://github.com/monero-project/monero/blob/master/src/cryptonote_basic/cryptonote_format_utils.cpp#L767") 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 blocktemplate_blob = block_template.serialize();
let blockhashing_blob = block_template.serialize_pow_hash(); let blockhashing_blob = block_template.serialize_pow_hash();
let blocktemplate_blob = HexVec(blocktemplate_blob); let blocktemplate_blob = hex::encode(blocktemplate_blob);
let blockhashing_blob = HexVec(blockhashing_blob); let blockhashing_blob = hex::encode(blockhashing_blob);
let merkle_root = Hex(merkle_root); let merkle_root = Hex(merkle_root);
let aux_pow = aux_pow.into_vec(); 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> /// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3553-L3627>
async fn get_tx_ids_loose( async fn get_tx_ids_loose(
state: CupratedRpcHandler, state: CupratedRpcHandler,
request: GetTxIdsLooseRequest, request: GetTxIdsLooseRequest,
) -> Result<GetTxIdsLooseResponse, Error> { ) -> Result<GetTxIdsLooseResponse, Error> {
// TODO: this RPC call is not yet in the v0.18 branch.
return Err(anyhow!("Not implemented"));
Ok(GetTxIdsLooseResponse { Ok(GetTxIdsLooseResponse {
base: helper::response_base(false), 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, RESTRICTED_TRANSACTIONS_COUNT,
}; };
use cuprate_helper::cast::usize_to_u64; 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_p2p_core::{client::handshaker::builder::DummyAddressBook, ClearNet};
use cuprate_rpc_interface::RpcHandler; use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{ use cuprate_rpc_types::{
@ -157,23 +157,23 @@ async fn get_transactions(
let prunable_hash = Hex(tx.prunable_hash); let prunable_hash = Hex(tx.prunable_hash);
let (pruned_as_hex, prunable_as_hex) = if tx.pruned_blob.is_empty() { let (pruned_as_hex, prunable_as_hex) = if tx.pruned_blob.is_empty() {
(HexVec::new(), HexVec::new()) (String::new(), String::new())
} else { } 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() { let as_hex = if pruned_as_hex.is_empty() {
// `monerod` will insert a `""` into the `txs_as_hex` array for pruned transactions. // `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' // curl http://127.0.0.1:18081/get_transactions -d '{"txs_hashes":["4c8b98753d1577d225a497a50f453827cff3aa023a4add60ec4ce4f923f75de8"]}' -H 'Content-Type: application/json'
HexVec::new() String::new()
} else { } else {
HexVec(tx.tx_blob) hex::encode(&tx.tx_blob)
}; };
txs_as_hex.push(as_hex.clone()); txs_as_hex.push(as_hex.clone());
let as_json = if request.decode_as_json { 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_type = cuprate_types::json::tx::Transaction::from(tx);
let json = serde_json::to_string(&json_type).unwrap(); let json = serde_json::to_string(&json_type).unwrap();
txs_as_json.push(json.clone()); txs_as_json.push(json.clone());
@ -217,11 +217,11 @@ async fn get_transactions(
let tx_hash = Hex(tx_hash); let tx_hash = Hex(tx_hash);
let tx = Transaction::read(&mut tx_blob.as_slice())?; let tx = Transaction::read(&mut tx_blob.as_slice())?;
let pruned_as_hex = HexVec::new(); let pruned_as_hex = String::new();
let prunable_as_hex = HexVec::new(); let prunable_as_hex = String::new();
let prunable_hash = Hex([0; 32]); 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()); txs_as_hex.push(as_hex.clone());
let as_json = if request.decode_as_json { let as_json = if request.decode_as_json {
@ -351,7 +351,10 @@ async fn send_raw_transaction(
tx_extra_too_big: false, 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 { if request.do_sanity_checks {
/// FIXME: these checks could be defined elsewhere. /// FIXME: these checks could be defined elsewhere.

View file

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

View file

@ -8,7 +8,7 @@ use bytes::{Buf, BufMut, Bytes, BytesMut};
use cuprate_fixed_bytes::{ByteArray, ByteArrayVec}; use cuprate_fixed_bytes::{ByteArray, ByteArrayVec};
use cuprate_helper::cast::u64_to_usize; use cuprate_helper::cast::u64_to_usize;
use cuprate_hex::{Hex, HexVec}; use cuprate_hex::Hex;
use crate::{ use crate::{
io::{checked_read_primitive, checked_write_primitive}, 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 { macro_rules! epee_seq {
($val:ty) => { ($val:ty) => {
impl EpeeValue for Vec<$val> { impl EpeeValue for Vec<$val> {
@ -515,7 +503,6 @@ epee_seq!(u16);
epee_seq!(f64); epee_seq!(f64);
epee_seq!(bool); epee_seq!(bool);
epee_seq!(Vec<u8>); epee_seq!(Vec<u8>);
epee_seq!(HexVec);
epee_seq!(String); epee_seq!(String);
epee_seq!(Bytes); epee_seq!(Bytes);
epee_seq!(BytesMut); epee_seq!(BytesMut);

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

@ -1,2 +1,7 @@
# `cuprate-hex` # `cuprate-hex`
Cuprate's hexadecimal data types. 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. // Allow some lints when running in debug mode.
#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] #![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))]
mod array; mod hex;
mod vec;
pub use array::Hex; pub use hex::Hex;
pub use vec::HexVec;

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);
}
}