Compare commits

...

5 commits

Author SHA1 Message Date
hinto.janai
bf5e863f18
remove ::<N>
Some checks failed
Deny / audit (push) Has been cancelled
2024-12-06 20:10:56 -05:00
hinto.janai
2de9a019c6
fix json-rpc handlers 2024-12-06 19:59:38 -05:00
hinto.janai
3c25374b03
architecture book 2024-12-06 19:30:17 -05:00
hinto.janai
f2e7af779f
move types, fix orphan impl + cyclic dependency 2024-12-06 19:19:14 -05:00
hinto.janai
5b8cacd6b3
!! 2024-12-06 18:02:05 -05:00
42 changed files with 1183 additions and 928 deletions

16
Cargo.lock generated
View file

@ -886,6 +886,7 @@ dependencies = [
"bytes", "bytes",
"cuprate-fixed-bytes", "cuprate-fixed-bytes",
"cuprate-helper", "cuprate-helper",
"cuprate-hex",
"hex", "hex",
"paste", "paste",
"ref-cast", "ref-cast",
@ -940,6 +941,15 @@ dependencies = [
"windows", "windows",
] ]
[[package]]
name = "cuprate-hex"
version = "0.0.0"
dependencies = [
"hex",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "cuprate-json-rpc" name = "cuprate-json-rpc"
version = "0.0.0" version = "0.0.0"
@ -1068,6 +1078,7 @@ dependencies = [
"cuprate-epee-encoding", "cuprate-epee-encoding",
"cuprate-fixed-bytes", "cuprate-fixed-bytes",
"cuprate-helper", "cuprate-helper",
"cuprate-hex",
"cuprate-p2p-core", "cuprate-p2p-core",
"cuprate-test-utils", "cuprate-test-utils",
"cuprate-types", "cuprate-types",
@ -1136,8 +1147,8 @@ dependencies = [
"cuprate-epee-encoding", "cuprate-epee-encoding",
"cuprate-fixed-bytes", "cuprate-fixed-bytes",
"cuprate-helper", "cuprate-helper",
"cuprate-hex",
"curve25519-dalek", "curve25519-dalek",
"hex",
"hex-literal", "hex-literal",
"monero-serai", "monero-serai",
"pretty_assertions", "pretty_assertions",
@ -1169,7 +1180,7 @@ name = "cuprate-zmq-types"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"assert-json-diff", "assert-json-diff",
"cuprate-types", "cuprate-hex",
"hex", "hex",
"serde", "serde",
"serde_json", "serde_json",
@ -1206,6 +1217,7 @@ dependencies = [
"cuprate-fast-sync", "cuprate-fast-sync",
"cuprate-fixed-bytes", "cuprate-fixed-bytes",
"cuprate-helper", "cuprate-helper",
"cuprate-hex",
"cuprate-json-rpc", "cuprate-json-rpc",
"cuprate-levin", "cuprate-levin",
"cuprate-p2p", "cuprate-p2p",

View file

@ -19,7 +19,6 @@ members = [
# Net # Net
"net/epee-encoding", "net/epee-encoding",
"net/fixed-bytes",
"net/levin", "net/levin",
"net/wire", "net/wire",
@ -28,8 +27,8 @@ members = [
"p2p/p2p-core", "p2p/p2p-core",
"p2p/bucket", "p2p/bucket",
"p2p/dandelion-tower", "p2p/dandelion-tower",
"p2p/async-buffer",
"p2p/address-book", "p2p/address-book",
"p2p/async-buffer",
# Storage # Storage
"storage/blockchain", "storage/blockchain",
@ -37,6 +36,11 @@ members = [
"storage/txpool", "storage/txpool",
"storage/database", "storage/database",
# Types
"types/types",
"types/hex",
"types/fixed-bytes",
# RPC # RPC
"rpc/json-rpc", "rpc/json-rpc",
"rpc/types", "rpc/types",
@ -51,7 +55,6 @@ members = [
"helper", "helper",
"pruning", "pruning",
"test-utils", "test-utils",
"types",
] ]
[profile.release] [profile.release]
@ -85,14 +88,13 @@ cuprate-consensus-context = { path = "consensus/context", default-featur
cuprate-cryptonight = { path = "cryptonight", default-features = false } cuprate-cryptonight = { path = "cryptonight", default-features = false }
cuprate-helper = { path = "helper", default-features = false } cuprate-helper = { path = "helper", default-features = false }
cuprate-epee-encoding = { path = "net/epee-encoding", default-features = false } cuprate-epee-encoding = { path = "net/epee-encoding", default-features = false }
cuprate-fixed-bytes = { path = "net/fixed-bytes", default-features = false }
cuprate-levin = { path = "net/levin", default-features = false } cuprate-levin = { path = "net/levin", default-features = false }
cuprate-wire = { path = "net/wire", default-features = false } cuprate-wire = { path = "net/wire", default-features = false }
cuprate-async-buffer = { path = "p2p/async-buffer", default-features = false }
cuprate-p2p = { path = "p2p/p2p", default-features = false } cuprate-p2p = { path = "p2p/p2p", default-features = false }
cuprate-p2p-core = { path = "p2p/p2p-core", default-features = false } cuprate-p2p-core = { path = "p2p/p2p-core", default-features = false }
cuprate-p2p-bucket = { path = "p2p/p2p-bucket", default-features = false } cuprate-p2p-bucket = { path = "p2p/p2p-bucket", default-features = false }
cuprate-dandelion-tower = { path = "p2p/dandelion-tower", default-features = false } cuprate-dandelion-tower = { path = "p2p/dandelion-tower", default-features = false }
cuprate-async-buffer = { path = "p2p/async-buffer", default-features = false }
cuprate-address-book = { path = "p2p/address-book", default-features = false } cuprate-address-book = { path = "p2p/address-book", default-features = false }
cuprate-blockchain = { path = "storage/blockchain", default-features = false } cuprate-blockchain = { path = "storage/blockchain", default-features = false }
cuprate-database = { path = "storage/database", default-features = false } cuprate-database = { path = "storage/database", default-features = false }
@ -100,7 +102,9 @@ cuprate-database-service = { path = "storage/service", default-featur
cuprate-txpool = { path = "storage/txpool", default-features = false } cuprate-txpool = { path = "storage/txpool", default-features = false }
cuprate-pruning = { path = "pruning", default-features = false } cuprate-pruning = { path = "pruning", default-features = false }
cuprate-test-utils = { path = "test-utils", default-features = false } cuprate-test-utils = { path = "test-utils", default-features = false }
cuprate-types = { path = "types", default-features = false } cuprate-types = { path = "types/types", default-features = false }
cuprate-hex = { path = "types/hex", default-features = false }
cuprate-fixed-bytes = { path = "types/fixed-bytes", default-features = false }
cuprate-json-rpc = { path = "rpc/json-rpc", default-features = false } cuprate-json-rpc = { path = "rpc/json-rpc", default-features = false }
cuprate-rpc-types = { path = "rpc/types", default-features = false } cuprate-rpc-types = { path = "rpc/types", default-features = false }
cuprate-rpc-interface = { path = "rpc/interface", default-features = false } cuprate-rpc-interface = { path = "rpc/interface", default-features = false }

View file

@ -32,6 +32,7 @@ cuprate-database = { workspace = true, features = ["serde"] }
cuprate-pruning = { workspace = true } cuprate-pruning = { workspace = true }
cuprate-test-utils = { workspace = true } cuprate-test-utils = { workspace = true }
cuprate-types = { workspace = true, features = ["json"] } cuprate-types = { workspace = true, features = ["json"] }
cuprate-hex = { workspace = true }
cuprate-json-rpc = { workspace = true } cuprate-json-rpc = { workspace = true }
cuprate-rpc-interface = { workspace = true } cuprate-rpc-interface = { workspace = true }
cuprate-rpc-types = { workspace = true, features = ["from"] } cuprate-rpc-types = { workspace = true, features = ["from"] }

View file

@ -66,7 +66,7 @@ pub enum BlockchainManagerRequest {
/// Number of the blocks to be generated. /// Number of the blocks to be generated.
amount_of_blocks: u64, amount_of_blocks: u64,
/// The previous block's hash. /// The previous block's hash.
prev_block: [u8; 32], prev_block: Option<[u8; 32]>,
/// The starting value for the nonce. /// The starting value for the nonce.
starting_nonce: u32, starting_nonce: u32,
/// The address that will receive the coinbase reward. /// The address that will receive the coinbase reward.

View file

@ -22,6 +22,7 @@ use cuprate_helper::{
cast::{u32_to_usize, u64_to_usize, usize_to_u64}, cast::{u32_to_usize, u64_to_usize, usize_to_u64},
map::split_u128_into_low_high_bits, map::split_u128_into_low_high_bits,
}; };
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::{
@ -51,7 +52,6 @@ use cuprate_rpc_types::{
CORE_RPC_VERSION, CORE_RPC_VERSION,
}; };
use cuprate_types::{ use cuprate_types::{
hex::Hex,
rpc::{AuxPow, CoinbaseTxSum, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry}, rpc::{AuxPow, CoinbaseTxSum, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry},
HardFork, HardFork,
}; };
@ -260,7 +260,7 @@ async fn submit_block(
let [blob] = request.block_blob; let [blob] = request.block_blob;
let bytes = hex::decode(blob)?; let bytes = hex::decode(blob)?;
let block = Block::read(&mut bytes.as_slice())?; let block = Block::read(&mut bytes.as_slice())?;
let block_id = hex::encode(block.hash()); let block_id = Hex(block.hash());
// Attempt to relay the block. // Attempt to relay the block.
blockchain_manager::relay_block(&mut state.blockchain_manager, Box::new(block)).await?; blockchain_manager::relay_block(&mut state.blockchain_manager, Box::new(block)).await?;
@ -283,7 +283,11 @@ async fn generate_blocks(
// FIXME: // FIXME:
// is this field only used as a local variable in the handler in `monerod`? // is this field only used as a local variable in the handler in `monerod`?
// It may not be needed in the request type. // It may not be needed in the request type.
let prev_block = helper::hex_to_hash(request.prev_block)?; let prev_block = if request.prev_block.is_empty() {
None
} else {
Some(helper::hex_to_hash(request.prev_block)?)
};
let (blocks, height) = blockchain_manager::generate_blocks( let (blocks, height) = blockchain_manager::generate_blocks(
&mut state.blockchain_manager, &mut state.blockchain_manager,
@ -294,7 +298,7 @@ async fn generate_blocks(
) )
.await?; .await?;
let blocks = blocks.into_iter().map(hex::encode).collect(); let blocks = blocks.into_iter().map(Hex).collect();
Ok(GenerateBlocksResponse { Ok(GenerateBlocksResponse {
base: ResponseBase::OK, base: ResponseBase::OK,
@ -342,8 +346,7 @@ async fn get_block_header_by_hash(
// 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::hex_to_hash(hash)?; let hash = get(&mut state, hash.0, request.fill_pow_hash).await?;
let hash = get(&mut state, hash, request.fill_pow_hash).await?;
block_headers.push(hash); block_headers.push(hash);
} }
@ -444,8 +447,8 @@ async fn get_block(
}; };
let blob = hex::encode(block.serialize()); let blob = hex::encode(block.serialize());
let miner_tx_hash = hex::encode(block.miner_transaction.hash()); let miner_tx_hash = Hex(block.miner_transaction.hash());
let tx_hashes = block.transactions.iter().map(hex::encode).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)?
@ -545,7 +548,7 @@ async fn get_info(
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::encode(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 { let update_available = if restricted {
@ -754,8 +757,8 @@ async fn flush_transaction_pool(
let tx_hashes = request let tx_hashes = request
.txids .txids
.into_iter() .into_iter()
.map(helper::hex_to_hash) .map(|h| h.0)
.collect::<Result<Vec<[u8; 32]>, _>>()?; .collect::<Vec<[u8; 32]>>();
txpool::flush(&mut state.txpool_manager, tx_hashes).await?; txpool::flush(&mut state.txpool_manager, tx_hashes).await?;
@ -896,8 +899,8 @@ async fn relay_tx(
let tx_hashes = request let tx_hashes = request
.txids .txids
.into_iter() .into_iter()
.map(helper::hex_to_hash) .map(|h| h.0)
.collect::<Result<Vec<[u8; 32]>, _>>()?; .collect::<Vec<[u8; 32]>>();
txpool::relay(&mut state.txpool_manager, tx_hashes).await?; txpool::relay(&mut state.txpool_manager, tx_hashes).await?;
@ -1009,8 +1012,8 @@ async fn get_miner_data(
let major_version = c.current_hf.as_u8(); let major_version = c.current_hf.as_u8();
let height = usize_to_u64(c.chain_height); let height = usize_to_u64(c.chain_height);
let prev_id = hex::encode(c.top_hash); let prev_id = Hex(c.top_hash);
let seed_hash = hex::encode(c.top_hash); let seed_hash = Hex(c.top_hash);
let difficulty = format!("{:#x}", c.next_difficulty); let difficulty = format!("{:#x}", c.next_difficulty);
let median_weight = usize_to_u64(c.median_weight_for_block_reward); let median_weight = usize_to_u64(c.median_weight_for_block_reward);
let already_generated_coins = c.already_generated_coins; let already_generated_coins = c.already_generated_coins;
@ -1018,7 +1021,7 @@ async fn get_miner_data(
.await? .await?
.into_iter() .into_iter()
.map(|entry| GetMinerDataTxBacklogEntry { .map(|entry| GetMinerDataTxBacklogEntry {
id: hex::encode(entry.id), id: Hex(entry.id),
weight: entry.weight, weight: entry.weight,
fee: entry.fee, fee: entry.fee,
}) })
@ -1062,7 +1065,7 @@ async fn calc_pow(
let hardfork = HardFork::from_version(request.major_version)?; let hardfork = HardFork::from_version(request.major_version)?;
let block_blob: Vec<u8> = hex::decode(request.block_blob)?; let block_blob: Vec<u8> = hex::decode(request.block_blob)?;
let block = Block::read(&mut block_blob.as_slice())?; let block = Block::read(&mut block_blob.as_slice())?;
let seed_hash = helper::hex_to_hash(request.seed_hash)?; let seed_hash = request.seed_hash.0;
// let block_weight = todo!(); // let block_weight = todo!();
@ -1087,9 +1090,9 @@ async fn calc_pow(
) )
.await?; .await?;
let pow_hash = hex::encode(pow_hash); Ok(CalcPowResponse {
pow_hash: Hex(pow_hash),
Ok(CalcPowResponse { pow_hash }) })
} }
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3542-L3551> /// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3542-L3551>

View file

@ -149,7 +149,7 @@ pub(crate) async fn target_height(
pub(crate) async fn generate_blocks( pub(crate) async fn generate_blocks(
blockchain_manager: &mut BlockchainManagerHandle, blockchain_manager: &mut BlockchainManagerHandle,
amount_of_blocks: u64, amount_of_blocks: u64,
prev_block: [u8; 32], prev_block: Option<[u8; 32]>,
starting_nonce: u32, starting_nonce: u32,
wallet_address: String, wallet_address: String,
) -> Result<(Vec<[u8; 32]>, u64), Error> { ) -> Result<(Vec<[u8; 32]>, u64), Error> {

View file

@ -24,7 +24,6 @@ cargo doc --open --package cuprate-blockchain
| Crate | In-tree path | Purpose | | Crate | In-tree path | Purpose |
|-------|--------------|---------| |-------|--------------|---------|
| [`cuprate-epee-encoding`](https://doc.cuprate.org/cuprate_epee_encoding) | [`net/epee-encoding/`](https://github.com/Cuprate/cuprate/tree/main/net/epee-encoding) | Epee (de)serialization | [`cuprate-epee-encoding`](https://doc.cuprate.org/cuprate_epee_encoding) | [`net/epee-encoding/`](https://github.com/Cuprate/cuprate/tree/main/net/epee-encoding) | Epee (de)serialization
| [`cuprate-fixed-bytes`](https://doc.cuprate.org/cuprate_fixed_bytes) | [`net/fixed-bytes/`](https://github.com/Cuprate/cuprate/tree/main/net/fixed-bytes) | Fixed byte containers backed by `byte::Byte`
| [`cuprate-levin`](https://doc.cuprate.org/cuprate_levin) | [`net/levin/`](https://github.com/Cuprate/cuprate/tree/main/net/levin) | Levin bucket protocol implementation | [`cuprate-levin`](https://doc.cuprate.org/cuprate_levin) | [`net/levin/`](https://github.com/Cuprate/cuprate/tree/main/net/levin) | Levin bucket protocol implementation
| [`cuprate-wire`](https://doc.cuprate.org/cuprate_wire) | [`net/wire/`](https://github.com/Cuprate/cuprate/tree/main/net/wire) | TODO | [`cuprate-wire`](https://doc.cuprate.org/cuprate_wire) | [`net/wire/`](https://github.com/Cuprate/cuprate/tree/main/net/wire) | TODO
@ -46,6 +45,13 @@ cargo doc --open --package cuprate-blockchain
| [`cuprate-database-service`](https://doc.cuprate.org/cuprate_database_service) | [`storage/database-service/`](https://github.com/Cuprate/cuprate/tree/main/storage/database-service) | `tower::Service` + thread-pool abstraction built on-top of `cuprate-database` | [`cuprate-database-service`](https://doc.cuprate.org/cuprate_database_service) | [`storage/database-service/`](https://github.com/Cuprate/cuprate/tree/main/storage/database-service) | `tower::Service` + thread-pool abstraction built on-top of `cuprate-database`
| [`cuprate-txpool`](https://doc.cuprate.org/cuprate_txpool) | [`storage/txpool/`](https://github.com/Cuprate/cuprate/tree/main/storage/txpool) | Transaction pool database built on-top of `cuprate-database` & `cuprate-database-service` | [`cuprate-txpool`](https://doc.cuprate.org/cuprate_txpool) | [`storage/txpool/`](https://github.com/Cuprate/cuprate/tree/main/storage/txpool) | Transaction pool database built on-top of `cuprate-database` & `cuprate-database-service`
## Types
| Crate | In-tree path | Purpose |
|-------|--------------|---------|
| [`cuprate-types`](https://doc.cuprate.org/cuprate_types) | [`types/types/`](https://github.com/Cuprate/cuprate/tree/main/types/types) | General types used throughout Cuprate |
| [`cuprate-hex`](https://doc.cuprate.org/cuprate_hex) | [`types/hex/`](https://github.com/Cuprate/cuprate/tree/main/types/hex) | Hexadecimal data types |
| [`cuprate-fixed-bytes`](https://doc.cuprate.org/cuprate_fixed_bytes) | [`types/fixed-bytes/`](https://github.com/Cuprate/cuprate/tree/main/net/fixed-bytes) | Fixed byte containers backed by `byte::Byte`
## RPC ## RPC
| Crate | In-tree path | Purpose | | Crate | In-tree path | Purpose |
|-------|--------------|---------| |-------|--------------|---------|
@ -67,7 +73,6 @@ cargo doc --open --package cuprate-blockchain
| [`cuprate-pruning`](https://doc.cuprate.org/cuprate_pruning) | [`pruning/`](https://github.com/Cuprate/cuprate/tree/main/pruning) | Monero pruning logic/types | [`cuprate-pruning`](https://doc.cuprate.org/cuprate_pruning) | [`pruning/`](https://github.com/Cuprate/cuprate/tree/main/pruning) | Monero pruning logic/types
| [`cuprate-helper`](https://doc.cuprate.org/cuprate_helper) | [`helper/`](https://github.com/Cuprate/cuprate/tree/main/helper) | Kitchen-sink helper crate for Cuprate | [`cuprate-helper`](https://doc.cuprate.org/cuprate_helper) | [`helper/`](https://github.com/Cuprate/cuprate/tree/main/helper) | Kitchen-sink helper crate for Cuprate
| [`cuprate-test-utils`](https://doc.cuprate.org/cuprate_test_utils) | [`test-utils/`](https://github.com/Cuprate/cuprate/tree/main/test-utils) | Testing utilities for Cuprate | [`cuprate-test-utils`](https://doc.cuprate.org/cuprate_test_utils) | [`test-utils/`](https://github.com/Cuprate/cuprate/tree/main/test-utils) | Testing utilities for Cuprate
| [`cuprate-types`](https://doc.cuprate.org/cuprate_types) | [`types/`](https://github.com/Cuprate/cuprate/tree/main/types) | Shared types across Cuprate
## Benchmarks ## Benchmarks
| Crate | In-tree path | Purpose | | Crate | In-tree path | Purpose |

View file

@ -17,6 +17,7 @@ std = ["dep:thiserror", "bytes/std", "cuprate-fixed-bytes/std"]
[dependencies] [dependencies]
cuprate-helper = { workspace = true, default-features = false, features = ["cast"] } cuprate-helper = { workspace = true, default-features = false, features = ["cast"] }
cuprate-fixed-bytes = { workspace = true, default-features = false } cuprate-fixed-bytes = { workspace = true, default-features = false }
cuprate-hex = { workspace = true, default-features = false }
paste = "1.0.15" paste = "1.0.15"
ref-cast = "1.0.23" ref-cast = "1.0.23"

View file

@ -8,6 +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;
use crate::{ use crate::{
io::{checked_read_primitive, checked_write_primitive}, io::{checked_read_primitive, checked_write_primitive},
@ -401,6 +402,41 @@ impl<const N: usize> EpeeValue for Vec<[u8; N]> {
} }
} }
impl<const N: usize> EpeeValue for Hex<N> {
const MARKER: Marker = <[u8; N] as EpeeValue>::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> Result<Self> {
Ok(Self(<[u8; N] as EpeeValue>::read(r, marker)?))
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
<[u8; N] as EpeeValue>::write(self.0, w)
}
}
impl<const N: usize> EpeeValue for Vec<Hex<N>> {
const MARKER: Marker = Vec::<[u8; N]>::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> Result<Self> {
Ok(Vec::<[u8; N]>::read(r, marker)?
.into_iter()
.map(Hex)
.collect())
}
fn should_write(&self) -> bool {
!self.is_empty()
}
fn epee_default_value() -> Option<Self> {
Some(Self::new())
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_iterator(self.into_iter(), w)
}
}
macro_rules! epee_seq { macro_rules! epee_seq {
($val:ty) => { ($val:ty) => {
impl EpeeValue for Vec<$val> { impl EpeeValue for Vec<$val> {

View file

@ -22,8 +22,9 @@ from = [
] ]
[dependencies] [dependencies]
cuprate-epee-encoding = { workspace = true, optional = true }
cuprate-fixed-bytes = { workspace = true } cuprate-fixed-bytes = { workspace = true }
cuprate-hex = { workspace = true }
cuprate-epee-encoding = { workspace = true, optional = true }
cuprate-types = { workspace = true, default-features = false } cuprate-types = { workspace = true, default-features = false }
cuprate-helper = { workspace = true, optional = true, default-features = false } cuprate-helper = { workspace = true, optional = true, default-features = false }
cuprate-p2p-core = { workspace = true, optional = true, default-features = false } cuprate-p2p-core = { workspace = true, optional = true, default-features = false }

View file

@ -8,7 +8,6 @@
//! `height`, it will use [`default_height`] to fill that in. //! `height`, it will use [`default_height`] to fill that in.
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
// use cuprate_types::hex::Hex;
//---------------------------------------------------------------------------------------------------- TODO //---------------------------------------------------------------------------------------------------- TODO
/// Default [`bool`] type used in request/response types, `false`. /// Default [`bool`] type used in request/response types, `false`.
@ -29,12 +28,6 @@ pub(crate) const fn default_string() -> String {
String::new() String::new()
} }
// /// Default [`Hex`] type used in request/response types.
// #[inline]
// pub(crate) const fn default_hex<const N: usize>() -> Hex<N> {
// Hex([0; N])
// }
/// Default block height used in request/response types. /// Default block height used in request/response types.
#[inline] #[inline]
pub(crate) const fn default_height() -> u64 { pub(crate) const fn default_height() -> u64 {

View file

@ -8,14 +8,12 @@ use std::{
}; };
use cuprate_helper::{fmt::hex_prefix_u128, map::ipv4_from_u32}; use cuprate_helper::{fmt::hex_prefix_u128, map::ipv4_from_u32};
use cuprate_hex::Hex;
use cuprate_p2p_core::{ use cuprate_p2p_core::{
types::{ConnectionId, ConnectionInfo, SetBan, Span}, types::{ConnectionId, ConnectionInfo, SetBan, Span},
NetZoneAddress, NetZoneAddress,
}; };
use cuprate_types::{ use cuprate_types::rpc::{BlockHeader, ChainInfo, HistogramEntry, TxInfo};
hex::Hex,
rpc::{BlockHeader, ChainInfo, HistogramEntry, TxInfo},
};
impl From<BlockHeader> for crate::misc::BlockHeader { impl From<BlockHeader> for crate::misc::BlockHeader {
fn from(x: BlockHeader) -> Self { fn from(x: BlockHeader) -> Self {
@ -128,7 +126,7 @@ impl From<ChainInfo> for crate::misc::ChainInfo {
fn from(x: ChainInfo) -> Self { fn from(x: ChainInfo) -> Self {
Self { Self {
block_hash: Hex(x.block_hash), block_hash: Hex(x.block_hash),
block_hashes: x.block_hashes.into_iter().map(hex::encode).collect(), block_hashes: x.block_hashes.into_iter().map(Hex).collect(),
difficulty_top64: x.difficulty_top64, difficulty_top64: x.difficulty_top64,
difficulty: x.difficulty, difficulty: x.difficulty,
height: x.height, height: x.height,

View file

@ -6,10 +6,8 @@
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use cuprate_types::{ use cuprate_hex::Hex;
hex::Hex, use cuprate_types::rpc::{AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry};
rpc::{AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry},
};
use crate::{ use crate::{
base::{AccessResponseBase, ResponseBase}, base::{AccessResponseBase, ResponseBase},
@ -57,11 +55,6 @@ define_request_and_response! {
// The request type. // The request type.
// //
// If `Request {/* fields */}` is provided, a struct is generate as-is.
//
// If `Request {}` is specified here, it will create a `pub type YOUR_REQUEST_TYPE = ()`
// instead of a `struct`, see below in other macro definitions for an example.
//
// If there are any additional attributes (`/// docs` or `#[derive]`s) // If there are any additional attributes (`/// docs` or `#[derive]`s)
// for the struct, they go here. // for the struct, they go here.
Request { Request {
@ -147,9 +140,6 @@ define_request_and_response! {
core_rpc_server_commands_defs.h => 919..=933, core_rpc_server_commands_defs.h => 919..=933,
GetBlockCount (empty), GetBlockCount (empty),
// There are no request fields specified,
// this will cause the macro to generate a
// type alias to `()` instead of a `struct`.
Request {}, Request {},
ResponseBase { ResponseBase {
@ -168,8 +158,8 @@ define_request_and_response! {
#[repr(transparent)] #[repr(transparent)]
#[derive(Copy)] #[derive(Copy)]
Request { Request {
// This is `std::vector<u64>` in `monerod` but /// This is `std::vector<u64>` 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_height: [u64; 1], block_height: [u64; 1],
}, },
@ -197,7 +187,7 @@ define_request_and_response! {
// FIXME: `cuprate_test_utils` only has an `error` response for this. // FIXME: `cuprate_test_utils` only has an `error` response for this.
ResponseBase { ResponseBase {
block_id: String, block_id: Hex<32>,
} }
} }
@ -216,7 +206,7 @@ define_request_and_response! {
}, },
ResponseBase { ResponseBase {
blocks: Vec<String>, blocks: Vec<Hex<32>>,
height: u64, height: u64,
} }
} }
@ -246,7 +236,7 @@ define_request_and_response! {
Request { Request {
hash: Hex<32>, hash: Hex<32>,
hashes: Vec<String> = default_vec::<String>(), "default_vec", hashes: Vec<Hex<32>> = default_vec::<Hex<32>>(), "default_vec",
fill_pow_hash: bool = default_false(), "default_false", fill_pow_hash: bool = default_false(), "default_false",
}, },
@ -311,11 +301,11 @@ define_request_and_response! {
AccessResponseBase { AccessResponseBase {
blob: String, blob: String,
block_header: BlockHeader, block_header: BlockHeader,
/// `cuprate_rpc_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.
json: String, json: String,
miner_tx_hash: String, miner_tx_hash: Hex<32>,
tx_hashes: Vec<String> = default_vec::<String>(), "default_vec", tx_hashes: Vec<Hex<32>> = default_vec::<Hex<32>>(), "default_vec",
} }
} }
@ -372,7 +362,7 @@ define_request_and_response! {
target_height: u64, target_height: u64,
target: u64, target: u64,
testnet: bool, testnet: bool,
top_block_hash: String, top_block_hash: Hex<32>,
tx_count: u64, tx_count: u64,
tx_pool_size: u64, tx_pool_size: u64,
update_available: bool, update_available: bool,
@ -459,7 +449,7 @@ define_request_and_response! {
FlushTransactionPool (restricted), FlushTransactionPool (restricted),
Request { Request {
txids: Vec<String> = default_vec::<String>(), "default_vec", txids: Vec<Hex<32>> = default_vec::<Hex<32>>(), "default_vec",
}, },
#[repr(transparent)] #[repr(transparent)]
@ -564,7 +554,7 @@ define_request_and_response! {
RelayTx (restricted), RelayTx (restricted),
Request { Request {
txids: Vec<String>, txids: Vec<Hex<32>>,
}, },
#[repr(transparent)] #[repr(transparent)]
@ -641,8 +631,8 @@ define_request_and_response! {
ResponseBase { ResponseBase {
major_version: u8, major_version: u8,
height: u64, height: u64,
prev_id: String, prev_id: Hex<32>,
seed_hash: String, seed_hash: Hex<32>,
difficulty: String, difficulty: String,
median_weight: u64, median_weight: u64,
already_generated_coins: u64, already_generated_coins: u64,
@ -679,13 +669,13 @@ define_request_and_response! {
major_version: u8, major_version: u8,
height: u64, height: u64,
block_blob: String, block_blob: String,
seed_hash: String, seed_hash: Hex<32>,
}, },
#[cfg_attr(feature = "serde", serde(transparent))] #[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)] #[repr(transparent)]
Response { Response {
pow_hash: String, pow_hash: Hex<32>,
} }
} }
@ -1055,9 +1045,9 @@ mod test {
json::GENERATE_BLOCKS_RESPONSE, json::GENERATE_BLOCKS_RESPONSE,
GenerateBlocksResponse { GenerateBlocksResponse {
base: ResponseBase::OK, base: ResponseBase::OK,
blocks: vec![ blocks: vec![Hex(hex!(
"49b712db7760e3728586f8434ee8bc8d7b3d410dac6bb6e98bf5845c83b917e4".into(), "49b712db7760e3728586f8434ee8bc8d7b3d410dac6bb6e98bf5845c83b917e4"
], ))],
height: 9783, height: 9783,
}, },
); );
@ -1299,7 +1289,7 @@ mod test {
json::GET_BLOCK_REQUEST, json::GET_BLOCK_REQUEST,
GetBlockRequest { GetBlockRequest {
height: 2751506, height: 2751506,
hash: String::default(), hash: String::new(),
fill_pow_hash: false, fill_pow_hash: false,
}, },
); );
@ -1335,7 +1325,7 @@ mod test {
wide_difficulty: "0x490be69168".into() wide_difficulty: "0x490be69168".into()
}, },
json: "{\n \"major_version\": 16, \n \"minor_version\": 16, \n \"timestamp\": 1667941829, \n \"prev_id\": \"b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7\", \n \"nonce\": 4110909056, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 2751566, \n \"vin\": [ {\n \"gen\": {\n \"height\": 2751506\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 600000000000, \n \"target\": {\n \"tagged_key\": {\n \"key\": \"d7cbf826b665d7a532c316982dc8dbc24f285cbc18bbcc27c7164cd9b3277a85\", \n \"view_tag\": \"d0\"\n }\n }\n }\n ], \n \"extra\": [ 1, 159, 98, 157, 139, 54, 189, 22, 162, 191, 206, 62, 168, 12, 49, 220, 77, 135, 98, 198, 113, 101, 174, 194, 24, 69, 73, 78, 50, 183, 88, 47, 224, 2, 17, 0, 0, 0, 41, 122, 120, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}".into(), json: "{\n \"major_version\": 16, \n \"minor_version\": 16, \n \"timestamp\": 1667941829, \n \"prev_id\": \"b27bdecfc6cd0a46172d136c08831cf67660377ba992332363228b1b722781e7\", \n \"nonce\": 4110909056, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 2751566, \n \"vin\": [ {\n \"gen\": {\n \"height\": 2751506\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 600000000000, \n \"target\": {\n \"tagged_key\": {\n \"key\": \"d7cbf826b665d7a532c316982dc8dbc24f285cbc18bbcc27c7164cd9b3277a85\", \n \"view_tag\": \"d0\"\n }\n }\n }\n ], \n \"extra\": [ 1, 159, 98, 157, 139, 54, 189, 22, 162, 191, 206, 62, 168, 12, 49, 220, 77, 135, 98, 198, 113, 101, 174, 194, 24, 69, 73, 78, 50, 183, 88, 47, 224, 2, 17, 0, 0, 0, 41, 122, 120, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}".into(),
miner_tx_hash: "e49b854c5f339d7410a77f2a137281d8042a0ffc7ef9ab24cd670b67139b24cd".into(), miner_tx_hash: Hex(hex!("e49b854c5f339d7410a77f2a137281d8042a0ffc7ef9ab24cd670b67139b24cd")),
tx_hashes: vec![], tx_hashes: vec![],
}); });
} }
@ -1448,8 +1438,9 @@ mod test {
target: 120, target: 120,
target_height: 0, target_height: 0,
testnet: false, testnet: false,
top_block_hash: "bdf06d18ed1931a8ee62654e9b6478cc459bc7072628b8e36f4524d339552946" top_block_hash: Hex(hex!(
.into(), "bdf06d18ed1931a8ee62654e9b6478cc459bc7072628b8e36f4524d339552946"
)),
tx_count: 43205750, tx_count: 43205750,
tx_pool_size: 12, tx_pool_size: 12,
update_available: false, update_available: false,
@ -1562,9 +1553,9 @@ mod test {
test_json_request( test_json_request(
json::FLUSH_TRANSACTION_POOL_REQUEST, json::FLUSH_TRANSACTION_POOL_REQUEST,
FlushTransactionPoolRequest { FlushTransactionPoolRequest {
txids: vec![ txids: vec![Hex(hex!(
"dc16fa8eaffe1484ca9014ea050e13131d3acf23b419f33bb4cc0b32b6c49308".into(), "dc16fa8eaffe1484ca9014ea050e13131d3acf23b419f33bb4cc0b32b6c49308"
], ))],
}, },
); );
} }
@ -1693,10 +1684,9 @@ mod test {
block_hash: Hex(hex!( block_hash: Hex(hex!(
"4826c7d45d7cf4f02985b5c405b0e5d7f92c8d25e015492ce19aa3b209295dce" "4826c7d45d7cf4f02985b5c405b0e5d7f92c8d25e015492ce19aa3b209295dce"
)), )),
block_hashes: vec![ block_hashes: vec![Hex(hex!(
"4826c7d45d7cf4f02985b5c405b0e5d7f92c8d25e015492ce19aa3b209295dce" "4826c7d45d7cf4f02985b5c405b0e5d7f92c8d25e015492ce19aa3b209295dce"
.into(), ))],
],
difficulty: 357404825113208373, difficulty: 357404825113208373,
difficulty_top64: 0, difficulty_top64: 0,
height: 3167471, height: 3167471,
@ -1710,10 +1700,9 @@ mod test {
block_hash: Hex(hex!( block_hash: Hex(hex!(
"33ee476f5a1c5b9d889274cbbe171f5e0112df7ed69021918042525485deb401" "33ee476f5a1c5b9d889274cbbe171f5e0112df7ed69021918042525485deb401"
)), )),
block_hashes: vec![ block_hashes: vec![Hex(hex!(
"33ee476f5a1c5b9d889274cbbe171f5e0112df7ed69021918042525485deb401" "33ee476f5a1c5b9d889274cbbe171f5e0112df7ed69021918042525485deb401"
.into(), ))],
],
difficulty: 354736121711617293, difficulty: 354736121711617293,
difficulty_top64: 0, difficulty_top64: 0,
height: 3157465, height: 3157465,
@ -1733,9 +1722,9 @@ mod test {
test_json_request( test_json_request(
json::RELAY_TX_REQUEST, json::RELAY_TX_REQUEST,
RelayTxRequest { RelayTxRequest {
txids: vec![ txids: vec![Hex(hex!(
"9fd75c429cbe52da9a52f2ffc5fbd107fe7fd2099c0d8de274dc8a67e0c98613".into(), "9fd75c429cbe52da9a52f2ffc5fbd107fe7fd2099c0d8de274dc8a67e0c98613"
], ))],
}, },
); );
} }
@ -1870,20 +1859,25 @@ mod test {
height: 2731375, height: 2731375,
major_version: 16, major_version: 16,
median_weight: 300000, median_weight: 300000,
prev_id: "78d50c5894d187c4946d54410990ca59a75017628174a9e8c7055fa4ca5c7c6d".into(), prev_id: Hex(hex!(
seed_hash: "a6b869d50eca3a43ec26fe4c369859cf36ae37ce6ecb76457d31ffeb8a6ca8a6" "78d50c5894d187c4946d54410990ca59a75017628174a9e8c7055fa4ca5c7c6d"
.into(), )),
seed_hash: Hex(hex!(
"a6b869d50eca3a43ec26fe4c369859cf36ae37ce6ecb76457d31ffeb8a6ca8a6"
)),
tx_backlog: vec![ tx_backlog: vec![
GetMinerDataTxBacklogEntry { GetMinerDataTxBacklogEntry {
fee: 30700000, fee: 30700000,
id: "9868490d6bb9207fdd9cf17ca1f6c791b92ca97de0365855ea5c089f67c22208" id: Hex(hex!(
.into(), "9868490d6bb9207fdd9cf17ca1f6c791b92ca97de0365855ea5c089f67c22208"
)),
weight: 1535, weight: 1535,
}, },
GetMinerDataTxBacklogEntry { GetMinerDataTxBacklogEntry {
fee: 44280000, fee: 44280000,
id: "b6000b02bbec71e18ad704bcae09fb6e5ae86d897ced14a718753e76e86c0a0a" id: Hex(hex!(
.into(), "b6000b02bbec71e18ad704bcae09fb6e5ae86d897ced14a718753e76e86c0a0a"
)),
weight: 2214, weight: 2214,
}, },
], ],
@ -1917,7 +1911,7 @@ mod test {
major_version: 14, major_version: 14,
height: 2286447, height: 2286447,
block_blob: "0e0ed286da8006ecdc1aab3033cf1716c52f13f9d8ae0051615a2453643de94643b550d543becd0000000002abc78b0101ffefc68b0101fcfcf0d4b422025014bb4a1eade6622fd781cb1063381cad396efa69719b41aa28b4fce8c7ad4b5f019ce1dc670456b24a5e03c2d9058a2df10fec779e2579753b1847b74ee644f16b023c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051399a1bc46a846474f5b33db24eae173a26393b976054ee14f9feefe99925233802867097564c9db7a36af5bb5ed33ab46e63092bd8d32cef121608c3258edd55562812e21cc7e3ac73045745a72f7d74581d9a0849d6f30e8b2923171253e864f4e9ddea3acb5bc755f1c4a878130a70c26297540bc0b7a57affb6b35c1f03d8dbd54ece8457531f8cba15bb74516779c01193e212050423020e45aa2c15dcb".into(), block_blob: "0e0ed286da8006ecdc1aab3033cf1716c52f13f9d8ae0051615a2453643de94643b550d543becd0000000002abc78b0101ffefc68b0101fcfcf0d4b422025014bb4a1eade6622fd781cb1063381cad396efa69719b41aa28b4fce8c7ad4b5f019ce1dc670456b24a5e03c2d9058a2df10fec779e2579753b1847b74ee644f16b023c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051399a1bc46a846474f5b33db24eae173a26393b976054ee14f9feefe99925233802867097564c9db7a36af5bb5ed33ab46e63092bd8d32cef121608c3258edd55562812e21cc7e3ac73045745a72f7d74581d9a0849d6f30e8b2923171253e864f4e9ddea3acb5bc755f1c4a878130a70c26297540bc0b7a57affb6b35c1f03d8dbd54ece8457531f8cba15bb74516779c01193e212050423020e45aa2c15dcb".into(),
seed_hash: "d432f499205150873b2572b5f033c9c6e4b7c6f3394bd2dd93822cd7085e7307".into(), seed_hash: Hex(hex!("d432f499205150873b2572b5f033c9c6e4b7c6f3394bd2dd93822cd7085e7307")),
}); });
} }
@ -1926,7 +1920,9 @@ mod test {
test_json_response( test_json_response(
json::CALC_POW_RESPONSE, json::CALC_POW_RESPONSE,
CalcPowResponse { CalcPowResponse {
pow_hash: "d0402d6834e26fb94a9ce38c6424d27d2069896a9b8b1ce685d79936bca6e0a8".into(), pow_hash: Hex(hex!(
"d0402d6834e26fb94a9ce38c6424d27d2069896a9b8b1ce685d79936bca6e0a8"
)),
}, },
); );
} }

View file

@ -5,7 +5,8 @@
//! the [`crate::misc::ConnectionInfo`] struct defined here. //! the [`crate::misc::ConnectionInfo`] struct defined here.
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
use cuprate_types::{hex::Hex, HardFork}; use cuprate_hex::Hex;
use cuprate_types::HardFork;
#[cfg(any(feature = "epee", feature = "serde"))] #[cfg(any(feature = "epee", feature = "serde"))]
use crate::defaults::default_zero; use crate::defaults::default_zero;
@ -195,7 +196,7 @@ define_struct_and_impl_epee! {
/// Used in [`crate::json::GetAlternateChainsResponse`]. /// Used in [`crate::json::GetAlternateChainsResponse`].
ChainInfo { ChainInfo {
block_hash: Hex<32>, block_hash: Hex<32>,
block_hashes: Vec<String>, // TODO: Vec<Hex<32>> when it has epee block_hashes: Vec<Hex<32>>,
difficulty: u64, difficulty: u64,
difficulty_top64: u64, difficulty_top64: u64,
height: u64, height: u64,
@ -306,7 +307,7 @@ define_struct_and_impl_epee! {
/// Used in [`crate::other::GetTransactionPoolResponse`]. /// Used in [`crate::other::GetTransactionPoolResponse`].
SpentKeyImageInfo { SpentKeyImageInfo {
id_hash: Hex<32>, id_hash: Hex<32>,
txs_hashes: Vec<String>, // TODO: Vec<Hex<32>> when it has epee txs_hashes: Vec<Hex<32>>,
} }
} }

File diff suppressed because it is too large Load diff

22
types/hex/Cargo.toml Normal file
View file

@ -0,0 +1,22 @@
[package]
name = "cuprate-hex"
version = "0.0.0"
edition = "2021"
description = "Cuprate's hexadecimal data types"
license = "MIT"
authors = ["hinto-janai"]
repository = "https://github.com/Cuprate/cuprate/tree/main/types"
keywords = ["cuprate", "hex"]
[features]
default = []
[dependencies]
hex = { workspace = true, features = ["alloc", "serde"] }
serde = { workspace = true, features = ["std", "derive"] }
[dev-dependencies]
serde_json = { workspace = true, features = ["std"] }
[lints]
workspace = true

7
types/hex/README.md Normal file
View file

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

View file

@ -3,52 +3,46 @@
//! This module provides transparent wrapper types for //! This module provides transparent wrapper types for
//! arrays that (de)serialize from hexadecimal input/output. //! arrays that (de)serialize from hexadecimal input/output.
#[cfg(feature = "epee")] use hex::{FromHex, FromHexError};
use cuprate_epee_encoding::{error, macros::bytes, EpeeValue, Marker}; use serde::{Deserialize, Deserializer, Serialize};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Wrapper type for a byte array that (de)serializes from/to hexadecimal strings. /// Wrapper type for a byte array that (de)serializes from/to hexadecimal strings.
/// ///
/// ```rust
/// # use cuprate_types::hex::Hex;
/// let hash = [1; 32];
/// let hex_bytes = Hex::<32>(hash);
/// let expected_json = r#""0101010101010101010101010101010101010101010101010101010101010101""#;
///
/// let to_string = serde_json::to_string(&hex_bytes).unwrap();
/// assert_eq!(to_string, expected_json);
///
/// let from_str = serde_json::from_str::<Hex<32>>(expected_json).unwrap();
/// assert_eq!(hex_bytes, from_str);
/// ```
///
/// # Deserialization /// # Deserialization
/// This struct has a custom deserialization that only applies to certain /// This struct has a custom deserialization that only applies to certain
/// `N` lengths because [`hex::FromHex`] does not implement for a generic `N`: /// `N` lengths because [`FromHex`] does not implement for a generic `N`:
/// <https://docs.rs/hex/0.4.3/src/hex/lib.rs.html#220-230> /// <https://docs.rs/hex/0.4.3/src/hex/lib.rs.html#220-230>
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[cfg_attr(feature = "serde", derive(Serialize))] #[serde(transparent)]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)] #[repr(transparent)]
pub struct Hex<const N: usize>( pub struct Hex<const N: usize>(#[serde(with = "hex::serde")] pub [u8; N]);
#[cfg_attr(feature = "serde", serde(with = "hex::serde"))] pub [u8; N],
);
#[cfg(feature = "serde")]
impl<'de, const N: usize> Deserialize<'de> for Hex<N> impl<'de, const N: usize> Deserialize<'de> for Hex<N>
where where
[u8; N]: hex::FromHex, [u8; N]: FromHex,
<[u8; N] as hex::FromHex>::Error: std::fmt::Display, <[u8; N] as FromHex>::Error: std::fmt::Display,
{ {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: serde::Deserializer<'de>, D: Deserializer<'de>,
{ {
Ok(Self(hex::serde::deserialize(deserializer)?)) Ok(Self(hex::serde::deserialize(deserializer)?))
} }
} }
#[cfg(feature = "epee")]
impl<const N: usize> EpeeValue for Hex<N> {
const MARKER: Marker = <[u8; N] as EpeeValue>::MARKER;
fn read<B: bytes::Buf>(r: &mut B, marker: &Marker) -> error::Result<Self> {
Ok(Self(<[u8; N] as EpeeValue>::read(r, marker)?))
}
fn write<B: bytes::BufMut>(self, w: &mut B) -> error::Result<()> {
<[u8; N] as EpeeValue>::write(self.0, w)
}
}
// Default is not implemented for arrays >32, so we must do it manually. // Default is not implemented for arrays >32, so we must do it manually.
impl<const N: usize> Default for Hex<N> { impl<const N: usize> Default for Hex<N> {
fn default() -> Self { fn default() -> Self {
@ -69,18 +63,18 @@ impl<const N: usize> From<[u8; N]> for Hex<N> {
} }
impl<const N: usize> TryFrom<String> for Hex<N> { impl<const N: usize> TryFrom<String> for Hex<N> {
type Error = hex::FromHexError; type Error = FromHexError;
fn try_from(value: String) -> Result<Self, Self::Error> { fn try_from(value: String) -> Result<Self, Self::Error> {
let vec = hex::decode(value)?; let vec = hex::decode(value)?;
match <[u8; N]>::try_from(vec) { match <[u8; N]>::try_from(vec) {
Ok(s) => Ok(Self(s)), Ok(s) => Ok(Self(s)),
Err(_) => Err(hex::FromHexError::InvalidStringLength), Err(_) => Err(FromHexError::InvalidStringLength),
} }
} }
} }
impl<const N: usize> TryFrom<&str> for Hex<N> { impl<const N: usize> TryFrom<&str> for Hex<N> {
type Error = hex::FromHexError; type Error = FromHexError;
fn try_from(value: &str) -> Result<Self, Self::Error> { fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut bytes = [0; N]; let mut bytes = [0; N];
hex::decode_to_slice(value, &mut bytes).map(|()| Self(bytes)) hex::decode_to_slice(value, &mut bytes).map(|()| Self(bytes))
@ -92,10 +86,10 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn hex_bytes_32() { fn asdf() {
let hash = [1; 32]; let hash = [0; 32];
let hex_bytes = Hex::<32>(hash); let hex_bytes = Hex::<32>(hash);
let expected_json = r#""0101010101010101010101010101010101010101010101010101010101010101""#; let expected_json = r#""0000000000000000000000000000000000000000000000000000000000000000""#;
let to_string = serde_json::to_string(&hex_bytes).unwrap(); let to_string = serde_json::to_string(&hex_bytes).unwrap();
assert_eq!(to_string, expected_json); assert_eq!(to_string, expected_json);

7
types/hex/src/lib.rs Normal file
View file

@ -0,0 +1,7 @@
#![doc = include_str!("../README.md")]
// Allow some lints when running in debug mode.
#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))]
mod hex;
pub use hex::Hex;

View file

@ -9,27 +9,24 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/types"
keywords = ["cuprate", "types"] keywords = ["cuprate", "types"]
[features] [features]
default = ["blockchain", "epee", "serde", "json", "hex", "rpc"] default = ["blockchain", "epee", "serde", "json", "rpc"]
blockchain = ["rpc"] blockchain = ["rpc"]
epee = ["dep:cuprate-epee-encoding"] epee = ["dep:cuprate-epee-encoding"]
serde = ["dep:serde", "hex"] serde = ["dep:serde"]
proptest = ["dep:proptest", "dep:proptest-derive"] proptest = ["dep:proptest", "dep:proptest-derive"]
json = ["hex", "dep:cuprate-helper"] json = ["dep:cuprate-hex", "dep:cuprate-helper"]
# We sadly have no choice but to enable serde here as otherwise we will get warnings from the `hex` dep being unused. rpc = ["dep:cuprate-hex", "json"]
# This isn't too bad as `Hex` only makes sense with serde anyway.
hex = ["serde", "dep:hex"]
rpc = ["hex", "json"]
[dependencies] [dependencies]
cuprate-epee-encoding = { workspace = true, optional = true, features = ["std"] } cuprate-epee-encoding = { workspace = true, optional = true, features = ["std"] }
cuprate-helper = { workspace = true, optional = true, features = ["cast"] } cuprate-helper = { workspace = true, optional = true, features = ["cast"] }
cuprate-fixed-bytes = { workspace = true, features = ["std", "serde"] } cuprate-fixed-bytes = { workspace = true, features = ["std", "serde"] }
cuprate-hex = { workspace = true, optional = true }
bytes = { workspace = true } bytes = { workspace = true }
cfg-if = { workspace = true } cfg-if = { workspace = true }
curve25519-dalek = { workspace = true } curve25519-dalek = { workspace = true }
monero-serai = { workspace = true } monero-serai = { workspace = true }
hex = { workspace = true, features = ["serde", "alloc"], optional = true }
serde = { workspace = true, features = ["std", "derive"], optional = true } serde = { workspace = true, features = ["std", "derive"], optional = true }
strum = { workspace = true, features = ["derive"] } strum = { workspace = true, features = ["derive"] }
thiserror = { workspace = true } thiserror = { workspace = true }

View file

@ -11,4 +11,3 @@ This crate is a kitchen-sink for data types that are shared across Cuprate.
| `epee` | Enables `cuprate-epee-encoding` on types where applicable | `epee` | Enables `cuprate-epee-encoding` on types where applicable
| `proptest` | Enables `proptest::arbitrary::Arbitrary` on some types | `proptest` | Enables `proptest::arbitrary::Arbitrary` on some types
| `json` | Enables the `json` module, containing JSON representations of common Monero types | `json` | Enables the `json` module, containing JSON representations of common Monero types
| `hex` | Enables the `hex` module, containing the `HexBytes` type

View file

@ -6,11 +6,9 @@ use serde::{Deserialize, Serialize};
use monero_serai::{block, transaction}; use monero_serai::{block, transaction};
use cuprate_helper::cast::usize_to_u64; use cuprate_helper::cast::usize_to_u64;
use cuprate_hex::Hex;
use crate::{ use crate::json::output::{Output, TaggedKey, Target};
hex::Hex,
json::output::{Output, TaggedKey, Target},
};
/// JSON representation of a block. /// JSON representation of a block.
/// ///
@ -34,13 +32,13 @@ impl From<block::Block> for Block {
unreachable!("input is a miner tx, this should never fail"); unreachable!("input is a miner tx, this should never fail");
}; };
let tx_hashes = b.transactions.into_iter().map(Hex::<32>).collect(); let tx_hashes = b.transactions.into_iter().map(Hex).collect();
Self { Self {
major_version: b.header.hardfork_version, major_version: b.header.hardfork_version,
minor_version: b.header.hardfork_signal, minor_version: b.header.hardfork_signal,
timestamp: b.header.timestamp, timestamp: b.header.timestamp,
prev_id: Hex::<32>(b.header.previous), prev_id: Hex(b.header.previous),
nonce: b.header.nonce, nonce: b.header.nonce,
miner_tx, miner_tx,
tx_hashes, tx_hashes,
@ -101,15 +99,13 @@ impl TryFrom<transaction::Transaction> for MinerTransaction {
let target = match o.view_tag { let target = match o.view_tag {
Some(view_tag) => { Some(view_tag) => {
let tagged_key = TaggedKey { let tagged_key = TaggedKey {
key: Hex::<32>(o.key.0), key: Hex(o.key.0),
view_tag: Hex::<1>([view_tag]), view_tag: Hex([view_tag]),
}; };
Target::TaggedKey { tagged_key } Target::TaggedKey { tagged_key }
} }
None => Target::Key { None => Target::Key { key: Hex(o.key.0) },
key: Hex::<32>(o.key.0),
},
}; };
Output { amount, target } Output { amount, target }
@ -222,7 +218,7 @@ mod test {
major_version: 1, major_version: 1,
minor_version: 0, minor_version: 0,
timestamp: 1415690591, timestamp: 1415690591,
prev_id: Hex::<32>(hex!( prev_id: Hex(hex!(
"e97a0ab6307de9b9f9a9872263ef3e957976fb227eb9422c6854e989e5d5d34c" "e97a0ab6307de9b9f9a9872263ef3e957976fb227eb9422c6854e989e5d5d34c"
)), )),
nonce: 2147484616, nonce: 2147484616,
@ -237,25 +233,25 @@ mod test {
Output { Output {
amount: 47019296802, amount: 47019296802,
target: Target::Key { target: Target::Key {
key: Hex::<32>(hex!("3c1dcbf5b485987ecef4596bb700e32cbc7bd05964e3888ffc05f8a46bf5fc33")), key: Hex(hex!("3c1dcbf5b485987ecef4596bb700e32cbc7bd05964e3888ffc05f8a46bf5fc33")),
} }
}, },
Output { Output {
amount: 200000000000, amount: 200000000000,
target: Target::Key { target: Target::Key {
key: Hex::<32>(hex!("5810afc7a1b01a1c913eb6aab15d4a851cbc4a8cf0adf90bb80ac1a7ca9928aa")), key: Hex(hex!("5810afc7a1b01a1c913eb6aab15d4a851cbc4a8cf0adf90bb80ac1a7ca9928aa")),
} }
}, },
Output { Output {
amount: 3000000000000, amount: 3000000000000,
target: Target::Key { target: Target::Key {
key: Hex::<32>(hex!("520f49c5f2ce8456dc1a565f35ed3a5ccfff3a1210b340870a57d2749a81a2df")), key: Hex(hex!("520f49c5f2ce8456dc1a565f35ed3a5ccfff3a1210b340870a57d2749a81a2df")),
} }
}, },
Output { Output {
amount: 10000000000000, amount: 10000000000000,
target: Target::Key { target: Target::Key {
key: Hex::<32>(hex!("44d7705e62c76c2e349a474df6724aa1d9932092002b03a94f9c19d9d12b9427")), key: Hex(hex!("44d7705e62c76c2e349a474df6724aa1d9932092002b03a94f9c19d9d12b9427")),
} }
} }
], ],
@ -281,7 +277,7 @@ mod test {
major_version: 16, major_version: 16,
minor_version: 16, minor_version: 16,
timestamp: 1727293028, timestamp: 1727293028,
prev_id: Hex::<32>(hex!( prev_id: Hex(hex!(
"41b56c273d69def3294e56179de71c61808042d54c1e085078d21dbe99e81b6f" "41b56c273d69def3294e56179de71c61808042d54c1e085078d21dbe99e81b6f"
)), )),
nonce: 311, nonce: 311,
@ -296,10 +292,10 @@ mod test {
amount: 601012280000, amount: 601012280000,
target: Target::TaggedKey { target: Target::TaggedKey {
tagged_key: TaggedKey { tagged_key: TaggedKey {
key: Hex::<32>(hex!( key: Hex(hex!(
"8c0b16c6df02b9944b49f375d96a958a0fc5431c048879bb5bf25f64a1163b9e" "8c0b16c6df02b9944b49f375d96a958a0fc5431c048879bb5bf25f64a1163b9e"
)), )),
view_tag: Hex::<1>(hex!("88")), view_tag: Hex(hex!("88")),
}, },
}, },
}], }],
@ -312,43 +308,43 @@ mod test {
rct_signatures: MinerTransactionRctSignatures { r#type: 0 }, rct_signatures: MinerTransactionRctSignatures { r#type: 0 },
}, },
tx_hashes: vec![ tx_hashes: vec![
Hex::<32>(hex!( Hex(hex!(
"eab76986a0cbcae690d8499f0f616f783fd2c89c6f611417f18011950dbdab2e" "eab76986a0cbcae690d8499f0f616f783fd2c89c6f611417f18011950dbdab2e"
)), )),
Hex::<32>(hex!( Hex(hex!(
"57b19aa8c2cdbb6836cf13dd1e321a67860965c12e4418f3c30f58c8899a851e" "57b19aa8c2cdbb6836cf13dd1e321a67860965c12e4418f3c30f58c8899a851e"
)), )),
Hex::<32>(hex!( Hex(hex!(
"5340185432ab6b74fb21379f7e8d8f0e37f0882b2a7121fd7c08736f079e2edc" "5340185432ab6b74fb21379f7e8d8f0e37f0882b2a7121fd7c08736f079e2edc"
)), )),
Hex::<32>(hex!( Hex(hex!(
"01dc6d31db56d68116f5294c1b4f80b33b048b5cdfefcd904f23e6c0de3daff5" "01dc6d31db56d68116f5294c1b4f80b33b048b5cdfefcd904f23e6c0de3daff5"
)), )),
Hex::<32>(hex!( Hex(hex!(
"c9fb6a2730678203948fef2a49fa155b63f35a3649f3d32ed405a6806f3bbd56" "c9fb6a2730678203948fef2a49fa155b63f35a3649f3d32ed405a6806f3bbd56"
)), )),
Hex::<32>(hex!( Hex(hex!(
"af965cdd2a2315baf1d4a3d242f44fe07b1fd606d5f4853c9ff546ca6c12a5af" "af965cdd2a2315baf1d4a3d242f44fe07b1fd606d5f4853c9ff546ca6c12a5af"
)), )),
Hex::<32>(hex!( Hex(hex!(
"97bc9e047d25fae8c14ce6ec882224e7b722f5e79b62a2602a6bacebdac8547b" "97bc9e047d25fae8c14ce6ec882224e7b722f5e79b62a2602a6bacebdac8547b"
)), )),
Hex::<32>(hex!( Hex(hex!(
"28c46992eaf10dc0cceb313c30572d023432b7bd26e85e679bc8fe419533a7bf" "28c46992eaf10dc0cceb313c30572d023432b7bd26e85e679bc8fe419533a7bf"
)), )),
Hex::<32>(hex!( Hex(hex!(
"c32e3acde2ff2885c9cc87253b40d6827d167dfcc3022c72f27084fd98788062" "c32e3acde2ff2885c9cc87253b40d6827d167dfcc3022c72f27084fd98788062"
)), )),
Hex::<32>(hex!( Hex(hex!(
"19e66a47f075c7cccde8a7b52803119e089e33e3a4847cace0bd1d17b0d22bab" "19e66a47f075c7cccde8a7b52803119e089e33e3a4847cace0bd1d17b0d22bab"
)), )),
Hex::<32>(hex!( Hex(hex!(
"8e8ac560e77a1ee72e82a5eb6887adbe5979a10cd29cb2c2a3720ce87db43a70" "8e8ac560e77a1ee72e82a5eb6887adbe5979a10cd29cb2c2a3720ce87db43a70"
)), )),
Hex::<32>(hex!( Hex(hex!(
"b7ff5141524b5cca24de6780a5dbfdf71e7de1e062fd85f557fb3b43b8e285dc" "b7ff5141524b5cca24de6780a5dbfdf71e7de1e062fd85f557fb3b43b8e285dc"
)), )),
Hex::<32>(hex!( Hex(hex!(
"f09df0f113763ef9b9a2752ac293b478102f7cab03ef803a3d9db7585aea8912" "f09df0f113763ef9b9a2752ac293b478102f7cab03ef803a3d9db7585aea8912"
)), )),
], ],

View file

@ -7,7 +7,7 @@
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::hex::Hex; use cuprate_hex::Hex;
/// JSON representation of an output. /// JSON representation of an output.
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]

File diff suppressed because it is too large Load diff

View file

@ -35,9 +35,6 @@ pub mod blockchain;
#[cfg(feature = "json")] #[cfg(feature = "json")]
pub mod json; pub mod json;
#[cfg(feature = "hex")]
pub mod hex;
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(feature = "rpc")] { if #[cfg(feature = "rpc")] {
pub mod rpc; pub mod rpc;

View file

@ -1,8 +1,9 @@
//! Various types (in)directly used in RPC. //! Various types (in)directly used in RPC.
use cuprate_fixed_bytes::ByteArrayVec; use cuprate_fixed_bytes::ByteArrayVec;
use cuprate_hex::Hex;
use crate::{hex::Hex, AddressType, ConnectionState, HardFork}; use crate::{AddressType, ConnectionState, HardFork};
const fn default_string() -> String { const fn default_string() -> String {
String::new() String::new()
@ -21,7 +22,7 @@ macro_rules! monero_definition_link {
) => { ) => {
concat!( concat!(
"[Definition](https://github.com/monero-project/monero/blob/", "[Definition](https://github.com/monero-project/monero/blob/",
stringify!($commit), $commit,
"/src/", "/src/",
$file_path, $file_path,
"#L", "#L",
@ -263,7 +264,7 @@ define_struct_and_impl_epee! {
1016..=1027 1016..=1027
)] )]
GetMinerDataTxBacklogEntry { GetMinerDataTxBacklogEntry {
id: String, id: Hex<32>,
weight: u64, weight: u64,
fee: u64, fee: u64,
} }

View file

@ -10,7 +10,7 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/zmq/types"
[dependencies] [dependencies]
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
hex = { workspace = true, features = ["std", "serde"] } hex = { workspace = true, features = ["std", "serde"] }
cuprate-types = { workspace = true, features = ["hex"] } cuprate-hex = { workspace = true }
[dev-dependencies] [dev-dependencies]
serde_json = { workspace = true, features = ["std"] } serde_json = { workspace = true, features = ["std"] }

View file

@ -8,7 +8,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use cuprate_types::hex::Hex; use cuprate_hex::Hex;
/// ZMQ `json-full-txpool_add` packets contain an array of `TxPoolAdd`. /// ZMQ `json-full-txpool_add` packets contain an array of `TxPoolAdd`.
/// ///