diff --git a/Cargo.lock b/Cargo.lock index e277e3f7..41847c6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,6 +653,7 @@ version = "0.5.0" dependencies = [ "bytes", "cuprate-fixed-bytes", + "cuprate-helper", "hex", "paste", "ref-cast", @@ -720,6 +721,7 @@ version = "0.1.0" dependencies = [ "bitflags 2.5.0", "bytes", + "cuprate-helper", "futures", "proptest", "rand", @@ -803,6 +805,7 @@ dependencies = [ "cuprate-helper", "cuprate-json-rpc", "cuprate-rpc-types", + "cuprate-test-utils", "futures", "paste", "serde", @@ -818,12 +821,9 @@ version = "0.0.0" dependencies = [ "cuprate-epee-encoding", "cuprate-fixed-bytes", - "cuprate-json-rpc", "cuprate-test-utils", "cuprate-types", - "monero-serai", "paste", - "pretty_assertions", "serde", "serde_json", ] @@ -880,7 +880,6 @@ dependencies = [ name = "cuprate-types" version = "0.0.0" dependencies = [ - "borsh", "bytes", "cuprate-epee-encoding", "cuprate-fixed-bytes", @@ -900,6 +899,7 @@ dependencies = [ "bytes", "cuprate-epee-encoding", "cuprate-fixed-bytes", + "cuprate-helper", "cuprate-levin", "cuprate-types", "hex", diff --git a/Cargo.toml b/Cargo.toml index 06b49a0a..0a98eab6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -262,6 +262,7 @@ empty_structs_with_brackets = "deny" empty_enum_variants_with_brackets = "deny" empty_drop = "deny" clone_on_ref_ptr = "deny" +upper_case_acronyms = "deny" # Hot # inline_always = "deny" @@ -278,13 +279,15 @@ clone_on_ref_ptr = "deny" # allow_attributes_without_reason = "deny" # missing_assert_message = "deny" # missing_docs_in_private_items = "deny" -# undocumented_unsafe_blocks = "deny" +undocumented_unsafe_blocks = "deny" # multiple_unsafe_ops_per_block = "deny" # single_char_lifetime_names = "deny" # wildcard_enum_match_arm = "deny" [workspace.lints.rust] # Cold +future_incompatible = { level = "deny", priority = -1 } +nonstandard_style = { level = "deny", priority = -1 } absolute_paths_not_starting_with_crate = "deny" explicit_outlives_requirements = "deny" keyword_idents_2018 = "deny" @@ -305,10 +308,11 @@ ambiguous_glob_imports = "deny" unused_unsafe = "deny" # Warm -let_underscore_drop = "deny" +let_underscore = { level = "deny", priority = -1 } unreachable_pub = "deny" unused_qualifications = "deny" variant_size_differences = "deny" +non_camel_case_types = "deny" # Hot # unused_results = "deny" diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..cc94ec53 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +upper-case-acronyms-aggressive = true diff --git a/consensus/rules/Cargo.toml b/consensus/rules/Cargo.toml index 2cf03e39..8ba321d6 100644 --- a/consensus/rules/Cargo.toml +++ b/consensus/rules/Cargo.toml @@ -11,7 +11,7 @@ proptest = ["dep:proptest", "dep:proptest-derive", "cuprate-types/proptest"] rayon = ["dep:rayon"] [dependencies] -cuprate-helper = { path = "../../helper", default-features = false, features = ["std"] } +cuprate-helper = { path = "../../helper", default-features = false, features = ["std", "cast"] } cuprate-types = { path = "../../types", default-features = false } cuprate-cryptonight = {path = "../../cryptonight"} diff --git a/consensus/rules/src/transactions/tests.rs b/consensus/rules/src/transactions/tests.rs index cd0e8c3b..4da8fd53 100644 --- a/consensus/rules/src/transactions/tests.rs +++ b/consensus/rules/src/transactions/tests.rs @@ -9,6 +9,8 @@ use proptest::{collection::vec, prelude::*}; use monero_serai::transaction::Output; +use cuprate_helper::cast::u64_to_usize; + use super::*; use crate::decomposed_amount::DECOMPOSED_AMOUNTS; @@ -164,7 +166,7 @@ prop_compose! { if timebased || lock_height > 500_000_000 { Timelock::Time(time_for_time_lock) } else { - Timelock::Block(usize::try_from(lock_height).unwrap()) + Timelock::Block(u64_to_usize(lock_height)) } } } @@ -179,7 +181,7 @@ prop_compose! { match ty { 0 => Timelock::None, 1 => Timelock::Time(time_for_time_lock), - _ => Timelock::Block(usize::try_from(lock_height).unwrap()) + _ => Timelock::Block(u64_to_usize(lock_height)) } } } diff --git a/consensus/src/block/alt_block.rs b/consensus/src/block/alt_block.rs index 513697e9..b20b4f26 100644 --- a/consensus/src/block/alt_block.rs +++ b/consensus/src/block/alt_block.rs @@ -14,7 +14,7 @@ use cuprate_consensus_rules::{ miner_tx::MinerTxError, ConsensusError, }; -use cuprate_helper::asynch::rayon_spawn_async; +use cuprate_helper::{asynch::rayon_spawn_async, cast::u64_to_usize}; use cuprate_types::{ AltBlockInformation, Chain, ChainId, TransactionVerificationData, VerifiedTransactionInformation, @@ -24,7 +24,7 @@ use crate::{ block::{free::pull_ordered_transactions, PreparedBlock}, context::{ difficulty::DifficultyCache, - rx_vms::RandomXVM, + rx_vms::RandomXVm, weight::{self, BlockWeightsCache}, AltChainContextCache, AltChainRequestToken, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW, }, @@ -101,7 +101,7 @@ where // Check the alt block timestamp is in the correct range. if let Some(median_timestamp) = - difficulty_cache.median_timestamp(BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW.try_into().unwrap()) + difficulty_cache.median_timestamp(u64_to_usize(BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)) { check_timestamp(&prepped_block.block, median_timestamp).map_err(ConsensusError::Block)? }; @@ -195,7 +195,7 @@ async fn alt_rx_vm<C>( parent_chain: Chain, alt_chain_context: &mut AltChainContextCache, context_svc: C, -) -> Result<Option<Arc<RandomXVM>>, ExtendedConsensusError> +) -> Result<Option<Arc<RandomXVm>>, ExtendedConsensusError> where C: Service< BlockChainContextRequest, diff --git a/consensus/src/block/batch_prepare.rs b/consensus/src/block/batch_prepare.rs index 9974d6d1..d32cd765 100644 --- a/consensus/src/block/batch_prepare.rs +++ b/consensus/src/block/batch_prepare.rs @@ -15,7 +15,7 @@ use cuprate_helper::asynch::rayon_spawn_async; use crate::{ block::{free::pull_ordered_transactions, PreparedBlock, PreparedBlockExPow}, - context::rx_vms::RandomXVM, + context::rx_vms::RandomXVm, transactions::new_tx_verification_data, BlockChainContextRequest, BlockChainContextResponse, ExtendedConsensusError, VerifyBlockResponse, @@ -148,7 +148,7 @@ where tracing::debug!("New randomX seed in batch, initialising VM"); let new_vm = rayon_spawn_async(move || { - Arc::new(RandomXVM::new(&new_vm_seed).expect("RandomX VM gave an error on set up!")) + Arc::new(RandomXVm::new(&new_vm_seed).expect("RandomX VM gave an error on set up!")) }) .await; diff --git a/consensus/src/context.rs b/consensus/src/context.rs index 26be75c3..9e713046 100644 --- a/consensus/src/context.rs +++ b/consensus/src/context.rs @@ -33,7 +33,7 @@ mod tokens; use cuprate_types::Chain; use difficulty::DifficultyCache; -use rx_vms::RandomXVM; +use rx_vms::RandomXVm; use weight::BlockWeightsCache; pub(crate) use alt_chains::{sealed::AltChainRequestToken, AltChainContextCache}; @@ -236,7 +236,7 @@ pub enum BlockChainContextRequest { /// seed. /// /// This should include the seed used to init this VM and the VM. - NewRXVM(([u8; 32], Arc<RandomXVM>)), + NewRXVM(([u8; 32], Arc<RandomXVm>)), /// A request to add a new block to the cache. Update(NewBlockData), /// Pop blocks from the cache to the specified height. @@ -313,7 +313,7 @@ pub enum BlockChainContextResponse { /// Blockchain context response. Context(BlockChainContext), /// A map of seed height to RandomX VMs. - RxVms(HashMap<usize, Arc<RandomXVM>>), + RxVms(HashMap<usize, Arc<RandomXVm>>), /// A list of difficulties. BatchDifficulties(Vec<u128>), /// An alt chain context cache. @@ -321,7 +321,7 @@ pub enum BlockChainContextResponse { /// A difficulty cache for an alt chain. AltChainDifficultyCache(DifficultyCache), /// A randomX VM for an alt chain. - AltChainRxVM(Arc<RandomXVM>), + AltChainRxVM(Arc<RandomXVm>), /// A weight cache for an alt chain AltChainWeightCache(BlockWeightsCache), /// A generic Ok response. diff --git a/consensus/src/context/alt_chains.rs b/consensus/src/context/alt_chains.rs index 5586226b..937e847e 100644 --- a/consensus/src/context/alt_chains.rs +++ b/consensus/src/context/alt_chains.rs @@ -11,7 +11,7 @@ use cuprate_types::{ use crate::{ ExtendedConsensusError, __private::Database, - context::{difficulty::DifficultyCache, rx_vms::RandomXVM, weight::BlockWeightsCache}, + context::{difficulty::DifficultyCache, rx_vms::RandomXVm, weight::BlockWeightsCache}, }; pub(crate) mod sealed { @@ -32,7 +32,7 @@ pub struct AltChainContextCache { pub difficulty_cache: Option<DifficultyCache>, /// A cached RX VM. - pub cached_rx_vm: Option<(usize, Arc<RandomXVM>)>, + pub cached_rx_vm: Option<(usize, Arc<RandomXVm>)>, /// The chain height of the alt chain. pub chain_height: usize, diff --git a/consensus/src/context/rx_vms.rs b/consensus/src/context/rx_vms.rs index 01aa9738..b1ab102b 100644 --- a/consensus/src/context/rx_vms.rs +++ b/consensus/src/context/rx_vms.rs @@ -9,7 +9,7 @@ use std::{ }; use futures::{stream::FuturesOrdered, StreamExt}; -use randomx_rs::{RandomXCache, RandomXError, RandomXFlag, RandomXVM as VMInner}; +use randomx_rs::{RandomXCache, RandomXError, RandomXFlag, RandomXVM as VmInner}; use rayon::prelude::*; use thread_local::ThreadLocal; use tower::ServiceExt; @@ -33,16 +33,16 @@ const RX_SEEDS_CACHED: usize = 2; /// A multithreaded randomX VM. #[derive(Debug)] -pub struct RandomXVM { +pub struct RandomXVm { /// These RandomX VMs all share the same cache. - vms: ThreadLocal<VMInner>, + vms: ThreadLocal<VmInner>, /// The RandomX cache. cache: RandomXCache, /// The flags used to start the RandomX VMs. flags: RandomXFlag, } -impl RandomXVM { +impl RandomXVm { /// Create a new multithreaded randomX VM with the provided seed. pub fn new(seed: &[u8; 32]) -> Result<Self, RandomXError> { // TODO: allow passing in flags. @@ -50,7 +50,7 @@ impl RandomXVM { let cache = RandomXCache::new(flags, seed.as_slice())?; - Ok(RandomXVM { + Ok(RandomXVm { vms: ThreadLocal::new(), cache, flags, @@ -58,12 +58,12 @@ impl RandomXVM { } } -impl RandomX for RandomXVM { +impl RandomX for RandomXVm { type Error = RandomXError; fn calculate_hash(&self, buf: &[u8]) -> Result<[u8; 32], Self::Error> { self.vms - .get_or_try(|| VMInner::new(self.flags, Some(self.cache.clone()), None))? + .get_or_try(|| VmInner::new(self.flags, Some(self.cache.clone()), None))? .calculate_hash(buf) .map(|out| out.try_into().unwrap()) } @@ -72,17 +72,17 @@ impl RandomX for RandomXVM { /// The randomX VMs cache, keeps the VM needed to calculate the current block's PoW hash (if a VM is needed) and a /// couple more around this VM. #[derive(Clone, Debug)] -pub struct RandomXVMCache { +pub struct RandomXVmCache { /// The top [`RX_SEEDS_CACHED`] RX seeds. pub(crate) seeds: VecDeque<(usize, [u8; 32])>, /// The VMs for `seeds` (if after hf 12, otherwise this will be empty). - pub(crate) vms: HashMap<usize, Arc<RandomXVM>>, + pub(crate) vms: HashMap<usize, Arc<RandomXVm>>, /// A single cached VM that was given to us from a part of Cuprate. - pub(crate) cached_vm: Option<([u8; 32], Arc<RandomXVM>)>, + pub(crate) cached_vm: Option<([u8; 32], Arc<RandomXVm>)>, } -impl RandomXVMCache { +impl RandomXVmCache { #[instrument(name = "init_rx_vm_cache", level = "info", skip(database))] pub async fn init_from_chain_height<D: Database + Clone>( chain_height: usize, @@ -106,7 +106,7 @@ impl RandomXVMCache { .map(|(height, seed)| { ( *height, - Arc::new(RandomXVM::new(seed).expect("Failed to create RandomX VM!")), + Arc::new(RandomXVm::new(seed).expect("Failed to create RandomX VM!")), ) }) .collect() @@ -117,7 +117,7 @@ impl RandomXVMCache { HashMap::new() }; - Ok(RandomXVMCache { + Ok(RandomXVmCache { seeds, vms, cached_vm: None, @@ -125,7 +125,7 @@ impl RandomXVMCache { } /// Add a randomX VM to the cache, with the seed it was created with. - pub fn add_vm(&mut self, vm: ([u8; 32], Arc<RandomXVM>)) { + pub fn add_vm(&mut self, vm: ([u8; 32], Arc<RandomXVm>)) { self.cached_vm.replace(vm); } @@ -136,7 +136,7 @@ impl RandomXVMCache { height: usize, chain: Chain, database: D, - ) -> Result<Arc<RandomXVM>, ExtendedConsensusError> { + ) -> Result<Arc<RandomXVm>, ExtendedConsensusError> { let seed_height = randomx_seed_height(height); let BlockchainResponse::BlockHash(seed_hash) = database @@ -156,13 +156,13 @@ impl RandomXVMCache { } } - let alt_vm = rayon_spawn_async(move || Arc::new(RandomXVM::new(&seed_hash).unwrap())).await; + let alt_vm = rayon_spawn_async(move || Arc::new(RandomXVm::new(&seed_hash).unwrap())).await; Ok(alt_vm) } /// Get the main-chain RandomX VMs. - pub async fn get_vms(&mut self) -> HashMap<usize, Arc<RandomXVM>> { + pub async fn get_vms(&mut self) -> HashMap<usize, Arc<RandomXVm>> { match self.seeds.len().checked_sub(self.vms.len()) { // No difference in the amount of seeds to VMs. Some(0) => (), @@ -184,7 +184,7 @@ impl RandomXVMCache { } }; - rayon_spawn_async(move || Arc::new(RandomXVM::new(&next_seed_hash).unwrap())) + rayon_spawn_async(move || Arc::new(RandomXVm::new(&next_seed_hash).unwrap())) .await }; @@ -200,7 +200,7 @@ impl RandomXVMCache { seeds_clone .par_iter() .map(|(height, seed)| { - let vm = RandomXVM::new(seed).expect("Failed to create RandomX VM!"); + let vm = RandomXVm::new(seed).expect("Failed to create RandomX VM!"); let vm = Arc::new(vm); (*height, vm) }) diff --git a/consensus/src/context/task.rs b/consensus/src/context/task.rs index 8939446a..bc54285a 100644 --- a/consensus/src/context/task.rs +++ b/consensus/src/context/task.rs @@ -9,6 +9,7 @@ use tower::ServiceExt; use tracing::Instrument; use cuprate_consensus_rules::blocks::ContextToVerifyBlock; +use cuprate_helper::cast::u64_to_usize; use cuprate_types::{ blockchain::{BlockchainReadRequest, BlockchainResponse}, Chain, @@ -45,7 +46,7 @@ pub struct ContextTask<D: Database> { /// The weight cache. weight_cache: weight::BlockWeightsCache, /// The RX VM cache. - rx_vm_cache: rx_vms::RandomXVMCache, + rx_vm_cache: rx_vms::RandomXVmCache, /// The hard-fork state cache. hardfork_state: hardforks::HardForkState, @@ -127,7 +128,7 @@ impl<D: Database + Clone + Send + 'static> ContextTask<D> { let db = database.clone(); let rx_seed_handle = tokio::spawn(async move { - rx_vms::RandomXVMCache::init_from_chain_height(chain_height, ¤t_hf, db).await + rx_vms::RandomXVmCache::init_from_chain_height(chain_height, ¤t_hf, db).await }); let context_svc = ContextTask { @@ -168,9 +169,9 @@ impl<D: Database + Clone + Send + 'static> ContextTask<D> { .weight_cache .effective_median_block_weight(¤t_hf), top_hash: self.top_block_hash, - median_block_timestamp: self.difficulty_cache.median_timestamp( - usize::try_from(BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW).unwrap(), - ), + median_block_timestamp: self + .difficulty_cache + .median_timestamp(u64_to_usize(BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)), chain_height: self.chain_height, current_hf, next_difficulty: self.difficulty_cache.next_difficulty(¤t_hf), diff --git a/consensus/src/tests/context/rx_vms.rs b/consensus/src/tests/context/rx_vms.rs index f18a9b59..5c198cf6 100644 --- a/consensus/src/tests/context/rx_vms.rs +++ b/consensus/src/tests/context/rx_vms.rs @@ -9,7 +9,7 @@ use cuprate_consensus_rules::{ }; use crate::{ - context::rx_vms::{get_last_rx_seed_heights, RandomXVMCache}, + context::rx_vms::{get_last_rx_seed_heights, RandomXVmCache}, tests::mock_db::*, }; @@ -42,7 +42,7 @@ fn rx_heights_consistent() { async fn rx_vm_created_on_hf_12() { let db = DummyDatabaseBuilder::default().finish(Some(10)); - let mut cache = RandomXVMCache::init_from_chain_height(10, &HardFork::V11, db) + let mut cache = RandomXVmCache::init_from_chain_height(10, &HardFork::V11, db) .await .unwrap(); @@ -67,7 +67,7 @@ proptest! { let rt = Builder::new_multi_thread().enable_all().build().unwrap(); rt.block_on(async move { - let cache = RandomXVMCache::init_from_chain_height(10, &hf, db).await.unwrap(); + let cache = RandomXVmCache::init_from_chain_height(10, &hf, db).await.unwrap(); assert!(cache.seeds.len() == cache.vms.len() || hf < HardFork::V12); }); } diff --git a/helper/Cargo.toml b/helper/Cargo.toml index 59e4e71d..c74e40fd 100644 --- a/helper/Cargo.toml +++ b/helper/Cargo.toml @@ -10,14 +10,15 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/consensus" [features] # All features on by default. -default = ["std", "atomic", "asynch", "fs", "num", "map", "time", "thread", "constants"] +default = ["std", "atomic", "asynch", "cast", "fs", "num", "map", "time", "thread", "constants"] std = [] atomic = ["dep:crossbeam"] asynch = ["dep:futures", "dep:rayon"] +cast = [] constants = [] fs = ["dep:dirs"] num = [] -map = ["dep:monero-serai"] +map = ["cast", "dep:monero-serai"] time = ["dep:chrono", "std"] thread = ["std", "dep:target_os_lib"] @@ -39,3 +40,6 @@ target_os_lib = { package = "libc", version = "0.2.151", optional = true } [dev-dependencies] tokio = { workspace = true, features = ["full"] } + +[lints] +workspace = true \ No newline at end of file diff --git a/helper/src/asynch.rs b/helper/src/asynch.rs index ea89dd79..9868191b 100644 --- a/helper/src/asynch.rs +++ b/helper/src/asynch.rs @@ -19,7 +19,7 @@ pub struct InfallibleOneshotReceiver<T>(oneshot::Receiver<T>); impl<T> From<oneshot::Receiver<T>> for InfallibleOneshotReceiver<T> { fn from(value: oneshot::Receiver<T>) -> Self { - InfallibleOneshotReceiver(value) + Self(value) } } @@ -43,7 +43,7 @@ where { let (tx, rx) = oneshot::channel(); rayon::spawn(move || { - let _ = tx.send(f()); + drop(tx.send(f())); }); rx.await.expect("The sender must not be dropped") } @@ -62,7 +62,7 @@ mod test { #[tokio::test] // Assert that basic channel operations work. async fn infallible_oneshot_receiver() { - let (tx, rx) = futures::channel::oneshot::channel::<String>(); + let (tx, rx) = oneshot::channel::<String>(); let msg = "hello world!".to_string(); tx.send(msg.clone()).unwrap(); @@ -84,7 +84,7 @@ mod test { let barrier = Arc::new(Barrier::new(2)); let task = |barrier: &Barrier| barrier.wait(); - let b_2 = barrier.clone(); + let b_2 = Arc::clone(&barrier); let (tx, rx) = std::sync::mpsc::channel(); diff --git a/helper/src/atomic.rs b/helper/src/atomic.rs index f253737a..47958964 100644 --- a/helper/src/atomic.rs +++ b/helper/src/atomic.rs @@ -49,6 +49,8 @@ pub type AtomicF64 = AtomicCell<f64>; //---------------------------------------------------------------------------------------------------- TESTS #[cfg(test)] mod tests { + #![allow(clippy::float_cmp)] + use super::*; #[test] diff --git a/helper/src/cast.rs b/helper/src/cast.rs new file mode 100644 index 00000000..99b7f53e --- /dev/null +++ b/helper/src/cast.rs @@ -0,0 +1,86 @@ +//! Casting. +//! +//! This modules provides utilities for casting between types. +//! +//! `#[no_std]` compatible. + +#![allow(clippy::cast_possible_truncation)] + +#[rustfmt::skip] +//============================ SAFETY: DO NOT REMOVE ===========================// +// // +// // +// Only allow building 64-bit targets. // +// This allows us to assume 64-bit invariants in this file. // + #[cfg(not(target_pointer_width = "64"))] + compile_error!("Cuprate is only compatible with 64-bit CPUs"); +// // +// // +//============================ SAFETY: DO NOT REMOVE ===========================// + +//---------------------------------------------------------------------------------------------------- Free functions +/// Cast [`u64`] to [`usize`]. +#[inline(always)] +pub const fn u64_to_usize(u: u64) -> usize { + u as usize +} + +/// Cast [`u32`] to [`usize`]. +#[inline(always)] +pub const fn u32_to_usize(u: u32) -> usize { + u as usize +} + +/// Cast [`usize`] to [`u64`]. +#[inline(always)] +pub const fn usize_to_u64(u: usize) -> u64 { + u as u64 +} + +/// Cast [`i64`] to [`isize`]. +#[inline(always)] +pub const fn i64_to_isize(i: i64) -> isize { + i as isize +} + +/// Cast [`i32`] to [`isize`]. +#[inline(always)] +pub const fn i32_to_isize(i: i32) -> isize { + i as isize +} + +/// Cast [`isize`] to [`i64`]. +#[inline(always)] +pub const fn isize_to_i64(i: isize) -> i64 { + i as i64 +} + +//---------------------------------------------------------------------------------------------------- Tests +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn max_unsigned() { + assert_eq!(u32_to_usize(u32::MAX), usize::try_from(u32::MAX).unwrap()); + assert_eq!(usize_to_u64(u32_to_usize(u32::MAX)), u64::from(u32::MAX)); + + assert_eq!(u64_to_usize(u64::MAX), usize::MAX); + assert_eq!(usize_to_u64(u64_to_usize(u64::MAX)), u64::MAX); + + assert_eq!(usize_to_u64(usize::MAX), u64::MAX); + assert_eq!(u64_to_usize(usize_to_u64(usize::MAX)), usize::MAX); + } + + #[test] + fn max_signed() { + assert_eq!(i32_to_isize(i32::MAX), isize::try_from(i32::MAX).unwrap()); + assert_eq!(isize_to_i64(i32_to_isize(i32::MAX)), i64::from(i32::MAX)); + + assert_eq!(i64_to_isize(i64::MAX), isize::MAX); + assert_eq!(isize_to_i64(i64_to_isize(i64::MAX)), i64::MAX); + + assert_eq!(isize_to_i64(isize::MAX), i64::MAX); + assert_eq!(i64_to_isize(isize_to_i64(isize::MAX)), isize::MAX); + } +} diff --git a/helper/src/fs.rs b/helper/src/fs.rs index 7290361f..5d62a644 100644 --- a/helper/src/fs.rs +++ b/helper/src/fs.rs @@ -190,72 +190,41 @@ mod test { // - It must `ends_with()` the expected end PATH for the OS #[test] fn path_sanity_check() { - assert!(CUPRATE_CACHE_DIR.is_absolute()); - assert!(CUPRATE_CONFIG_DIR.is_absolute()); - assert!(CUPRATE_DATA_DIR.is_absolute()); - assert!(CUPRATE_BLOCKCHAIN_DIR.is_absolute()); + // Array of (PATH, expected_path_as_string). + // + // The different OS's will set the expected path below. + let mut array = [ + (&*CUPRATE_CACHE_DIR, ""), + (&*CUPRATE_CONFIG_DIR, ""), + (&*CUPRATE_DATA_DIR, ""), + (&*CUPRATE_BLOCKCHAIN_DIR, ""), + (&*CUPRATE_TXPOOL_DIR, ""), + ]; if cfg!(target_os = "windows") { - let dir = &*CUPRATE_CACHE_DIR; - println!("cuprate_cache_dir: {dir:?}"); - assert!(dir.ends_with(r"AppData\Local\Cuprate")); - - let dir = &*CUPRATE_CONFIG_DIR; - println!("cuprate_config_dir: {dir:?}"); - assert!(dir.ends_with(r"AppData\Roaming\Cuprate")); - - let dir = &*CUPRATE_DATA_DIR; - println!("cuprate_data_dir: {dir:?}"); - assert!(dir.ends_with(r"AppData\Roaming\Cuprate")); - - let dir = &*CUPRATE_BLOCKCHAIN_DIR; - println!("cuprate_blockchain_dir: {dir:?}"); - assert!(dir.ends_with(r"AppData\Roaming\Cuprate\blockchain")); - - let dir = &*CUPRATE_TXPOOL_DIR; - println!("cuprate_txpool_dir: {dir:?}"); - assert!(dir.ends_with(r"AppData\Roaming\Cuprate\txpool")); + array[0].1 = r"AppData\Local\Cuprate"; + array[1].1 = r"AppData\Roaming\Cuprate"; + array[2].1 = r"AppData\Roaming\Cuprate"; + array[3].1 = r"AppData\Roaming\Cuprate\blockchain"; + array[4].1 = r"AppData\Roaming\Cuprate\txpool"; } else if cfg!(target_os = "macos") { - let dir = &*CUPRATE_CACHE_DIR; - println!("cuprate_cache_dir: {dir:?}"); - assert!(dir.ends_with("Library/Caches/Cuprate")); - - let dir = &*CUPRATE_CONFIG_DIR; - println!("cuprate_config_dir: {dir:?}"); - assert!(dir.ends_with("Library/Application Support/Cuprate")); - - let dir = &*CUPRATE_DATA_DIR; - println!("cuprate_data_dir: {dir:?}"); - assert!(dir.ends_with("Library/Application Support/Cuprate")); - - let dir = &*CUPRATE_BLOCKCHAIN_DIR; - println!("cuprate_blockchain_dir: {dir:?}"); - assert!(dir.ends_with("Library/Application Support/Cuprate/blockchain")); - - let dir = &*CUPRATE_TXPOOL_DIR; - println!("cuprate_txpool_dir: {dir:?}"); - assert!(dir.ends_with("Library/Application Support/Cuprate/txpool")); + array[0].1 = "Library/Caches/Cuprate"; + array[1].1 = "Library/Application Support/Cuprate"; + array[2].1 = "Library/Application Support/Cuprate"; + array[3].1 = "Library/Application Support/Cuprate/blockchain"; + array[4].1 = "Library/Application Support/Cuprate/txpool"; } else { // Assumes Linux. - let dir = &*CUPRATE_CACHE_DIR; - println!("cuprate_cache_dir: {dir:?}"); - assert!(dir.ends_with(".cache/cuprate")); + array[0].1 = ".cache/cuprate"; + array[1].1 = ".config/cuprate"; + array[2].1 = ".local/share/cuprate"; + array[3].1 = ".local/share/cuprate/blockchain"; + array[4].1 = ".local/share/cuprate/txpool"; + }; - let dir = &*CUPRATE_CONFIG_DIR; - println!("cuprate_config_dir: {dir:?}"); - assert!(dir.ends_with(".config/cuprate")); - - let dir = &*CUPRATE_DATA_DIR; - println!("cuprate_data_dir: {dir:?}"); - assert!(dir.ends_with(".local/share/cuprate")); - - let dir = &*CUPRATE_BLOCKCHAIN_DIR; - println!("cuprate_blockchain_dir: {dir:?}"); - assert!(dir.ends_with(".local/share/cuprate/blockchain")); - - let dir = &*CUPRATE_TXPOOL_DIR; - println!("cuprate_txpool_dir: {dir:?}"); - assert!(dir.ends_with(".local/share/cuprate/txpool")); + for (path, expected) in array { + assert!(path.is_absolute()); + assert!(path.ends_with(expected)); } } } diff --git a/helper/src/lib.rs b/helper/src/lib.rs index 90f420d6..de0d9555 100644 --- a/helper/src/lib.rs +++ b/helper/src/lib.rs @@ -1,36 +1,4 @@ #![doc = include_str!("../README.md")] -//---------------------------------------------------------------------------------------------------- Lints -#![allow(clippy::len_zero, clippy::type_complexity, clippy::module_inception)] -#![deny(nonstandard_style, deprecated, missing_docs, unused_mut)] -#![forbid( - unused_unsafe, - future_incompatible, - break_with_label_and_loop, - coherence_leak_check, - duplicate_macro_attributes, - exported_private_dependencies, - for_loops_over_fallibles, - large_assignments, - overlapping_range_endpoints, - // private_in_public, - semicolon_in_expressions_from_macros, - redundant_semicolons, - unconditional_recursion, - unreachable_patterns, - unused_allocation, - unused_braces, - unused_comparisons, - unused_doc_comments, - unused_parens, - unused_labels, - while_true, - keyword_idents, - non_ascii_idents, - noop_method_call, - unreachable_pub, - single_use_lifetimes, - // variant_size_differences, -)] #![cfg_attr(not(feature = "std"), no_std)] //---------------------------------------------------------------------------------------------------- Public API @@ -40,6 +8,9 @@ pub mod asynch; // async collides #[cfg(feature = "atomic")] pub mod atomic; +#[cfg(feature = "cast")] +pub mod cast; + #[cfg(feature = "constants")] pub mod constants; diff --git a/helper/src/map.rs b/helper/src/map.rs index 96d9f615..7805ea66 100644 --- a/helper/src/map.rs +++ b/helper/src/map.rs @@ -7,6 +7,8 @@ //---------------------------------------------------------------------------------------------------- Use use monero_serai::transaction::Timelock; +use crate::cast::{u64_to_usize, usize_to_u64}; + //---------------------------------------------------------------------------------------------------- `(u64, u64) <-> u128` /// Split a [`u128`] value into 2 64-bit values. /// @@ -27,6 +29,7 @@ use monero_serai::transaction::Timelock; /// ``` #[inline] pub const fn split_u128_into_low_high_bits(value: u128) -> (u64, u64) { + #[allow(clippy::cast_possible_truncation)] (value as u64, (value >> 64) as u64) } @@ -58,7 +61,7 @@ pub const fn combine_low_high_bits_to_u128(low_bits: u64, high_bits: u64) -> u12 /// Map a [`u64`] to a [`Timelock`]. /// /// Height/time is not differentiated via type, but rather: -/// "height is any value less than 500_000_000 and timestamp is any value above" +/// "height is any value less than `500_000_000` and timestamp is any value above" /// so the `u64/usize` is stored without any tag. /// /// See [`timelock_to_u64`] for the inverse function. @@ -73,11 +76,11 @@ pub const fn combine_low_high_bits_to_u128(low_bits: u64, high_bits: u64) -> u12 /// assert_eq!(u64_to_timelock(499_999_999), Timelock::Block(499_999_999)); /// assert_eq!(u64_to_timelock(500_000_000), Timelock::Time(500_000_000)); /// ``` -pub fn u64_to_timelock(u: u64) -> Timelock { +pub const fn u64_to_timelock(u: u64) -> Timelock { if u == 0 { Timelock::None } else if u < 500_000_000 { - Timelock::Block(usize::try_from(u).unwrap()) + Timelock::Block(u64_to_usize(u)) } else { Timelock::Time(u) } @@ -94,10 +97,10 @@ pub fn u64_to_timelock(u: u64) -> Timelock { /// assert_eq!(timelock_to_u64(Timelock::Block(499_999_999)), 499_999_999); /// assert_eq!(timelock_to_u64(Timelock::Time(500_000_000)), 500_000_000); /// ``` -pub fn timelock_to_u64(timelock: Timelock) -> u64 { +pub const fn timelock_to_u64(timelock: Timelock) -> u64 { match timelock { Timelock::None => 0, - Timelock::Block(u) => u64::try_from(u).unwrap(), + Timelock::Block(u) => usize_to_u64(u), Timelock::Time(u) => u, } } diff --git a/helper/src/network.rs b/helper/src/network.rs index 684e71a4..f3224b33 100644 --- a/helper/src/network.rs +++ b/helper/src/network.rs @@ -30,11 +30,11 @@ pub enum Network { impl Network { /// Returns the network ID for the current network. - pub fn network_id(&self) -> [u8; 16] { + pub const fn network_id(&self) -> [u8; 16] { match self { - Network::Mainnet => MAINNET_NETWORK_ID, - Network::Testnet => TESTNET_NETWORK_ID, - Network::Stagenet => STAGENET_NETWORK_ID, + Self::Mainnet => MAINNET_NETWORK_ID, + Self::Testnet => TESTNET_NETWORK_ID, + Self::Stagenet => STAGENET_NETWORK_ID, } } } diff --git a/helper/src/num.rs b/helper/src/num.rs index f90357e9..674ed354 100644 --- a/helper/src/num.rs +++ b/helper/src/num.rs @@ -89,8 +89,9 @@ where /// assert_eq!(median(vec), 5); /// ``` /// -/// # Safety +/// # Invariant /// If not sorted the output will be invalid. +#[allow(clippy::debug_assert_with_mut_call)] pub fn median<T>(array: impl AsRef<[T]>) -> T where T: Add<Output = T> diff --git a/helper/src/thread.rs b/helper/src/thread.rs index 96958ff6..04a26069 100644 --- a/helper/src/thread.rs +++ b/helper/src/thread.rs @@ -28,10 +28,10 @@ macro_rules! impl_thread_percent { $( $(#[$doc])* pub fn $fn_name() -> NonZeroUsize { - // SAFETY: // unwrap here is okay because: // - THREADS().get() is always non-zero // - max() guards against 0 + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_precision_loss)] NonZeroUsize::new(max(1, (threads().get() as f64 * $percent).floor() as usize)).unwrap() } )* @@ -58,10 +58,10 @@ impl_thread_percent! { /// Originally from <https://docs.rs/lpt>. /// /// # Windows -/// Uses SetThreadPriority() with THREAD_PRIORITY_IDLE (-15). +/// Uses `SetThreadPriority()` with `THREAD_PRIORITY_IDLE` (-15). /// /// # Unix -/// Uses libc::nice() with the max nice level. +/// Uses `libc::nice()` with the max nice level. /// /// On macOS and *BSD: +20 /// On Linux: +19 @@ -74,7 +74,7 @@ pub fn low_priority_thread() { // SAFETY: calling C. // We are _lowering_ our priority, not increasing, so this function should never fail. unsafe { - let _ = SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); + drop(SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE)); } } @@ -87,7 +87,7 @@ pub fn low_priority_thread() { // SAFETY: calling C. // We are _lowering_ our priority, not increasing, so this function should never fail. unsafe { - let _ = libc::nice(NICE_MAX); + libc::nice(NICE_MAX); } } } diff --git a/helper/src/time.rs b/helper/src/time.rs index 28aff7f5..ce39c2dc 100644 --- a/helper/src/time.rs +++ b/helper/src/time.rs @@ -129,6 +129,7 @@ pub const fn secs_to_clock(seconds: u32) -> (u8, u8, u8) { debug_assert!(m < 60); debug_assert!(s < 60); + #[allow(clippy::cast_possible_truncation)] // checked above (h as u8, m, s) } @@ -153,6 +154,7 @@ pub fn time() -> u32 { /// /// This is guaranteed to return a value between `0..=86399` pub fn time_utc() -> u32 { + #[allow(clippy::cast_sign_loss)] // checked in function calls unix_clock(chrono::offset::Local::now().timestamp() as u64) } diff --git a/net/epee-encoding/Cargo.toml b/net/epee-encoding/Cargo.toml index 7feac004..85ee2c93 100644 --- a/net/epee-encoding/Cargo.toml +++ b/net/epee-encoding/Cargo.toml @@ -15,6 +15,7 @@ default = ["std"] std = ["dep:thiserror", "bytes/std", "cuprate-fixed-bytes/std"] [dependencies] +cuprate-helper = { path = "../../helper", default-features = false, features = ["cast"] } cuprate-fixed-bytes = { path = "../fixed-bytes", default-features = false } paste = "1.0.14" diff --git a/net/epee-encoding/src/lib.rs b/net/epee-encoding/src/lib.rs index 5b64315e..fa3449b4 100644 --- a/net/epee-encoding/src/lib.rs +++ b/net/epee-encoding/src/lib.rs @@ -65,6 +65,8 @@ use core::{ops::Deref, str::from_utf8 as str_from_utf8}; use bytes::{Buf, BufMut, Bytes, BytesMut}; +use cuprate_helper::cast::{u64_to_usize, usize_to_u64}; + pub mod container_as_blob; pub mod error; mod io; @@ -242,7 +244,7 @@ pub fn write_bytes<T: AsRef<[u8]>, B: BufMut>(t: T, w: &mut B) -> Result<()> { let bytes = t.as_ref(); let len = bytes.len(); - write_varint(len.try_into()?, w)?; + write_varint(usize_to_u64(len), w)?; if w.remaining_mut() < len { return Err(Error::IO("Not enough capacity to write bytes")); @@ -286,7 +288,7 @@ where I: Iterator<Item = T> + ExactSizeIterator, B: BufMut, { - write_varint(iterator.len().try_into()?, w)?; + write_varint(usize_to_u64(iterator.len()), w)?; for item in iterator.into_iter() { item.write(w)?; } @@ -334,7 +336,7 @@ fn skip_epee_value<B: Buf>(r: &mut B, skipped_objects: &mut u8) -> Result<()> { if let Some(size) = marker.inner_marker.size() { let bytes_to_skip = size - .checked_mul(len.try_into()?) + .checked_mul(u64_to_usize(len)) .ok_or(Error::Value("List is too big".to_string()))?; return advance(bytes_to_skip, r); }; @@ -352,8 +354,8 @@ fn skip_epee_value<B: Buf>(r: &mut B, skipped_objects: &mut u8) -> Result<()> { | InnerMarker::U8 | InnerMarker::Bool => unreachable!("These types are constant size."), InnerMarker::String => { - let len = read_varint(r)?; - advance(len.try_into()?, r)?; + let len = u64_to_usize(read_varint(r)?); + advance(len, r)?; } InnerMarker::Object => { *skipped_objects += 1; diff --git a/net/epee-encoding/src/value.rs b/net/epee-encoding/src/value.rs index 094f0ef1..000d89c7 100644 --- a/net/epee-encoding/src/value.rs +++ b/net/epee-encoding/src/value.rs @@ -7,6 +7,7 @@ use core::fmt::Debug; use bytes::{Buf, BufMut, Bytes, BytesMut}; use cuprate_fixed_bytes::{ByteArray, ByteArrayVec}; +use cuprate_helper::cast::u64_to_usize; use crate::{ io::{checked_read_primitive, checked_write_primitive}, @@ -66,11 +67,11 @@ impl<T: EpeeObject> EpeeValue for Vec<T> { "Marker is not sequence when a sequence was expected", )); } - let len = read_varint(r)?; + let len = u64_to_usize(read_varint(r)?); let individual_marker = Marker::new(marker.inner_marker); - let mut res = Vec::with_capacity(len.try_into()?); + let mut res = Vec::with_capacity(len); for _ in 0..len { res.push(T::read(r, &individual_marker)?); } @@ -167,11 +168,13 @@ impl EpeeValue for Vec<u8> { return Err(Error::Format("Byte array exceeded max length")); } - if r.remaining() < len.try_into()? { + let len = u64_to_usize(len); + + if r.remaining() < len { return Err(Error::IO("Not enough bytes to fill object")); } - let mut res = vec![0; len.try_into()?]; + let mut res = vec![0; len]; r.copy_to_slice(&mut res); Ok(res) @@ -203,11 +206,13 @@ impl EpeeValue for Bytes { return Err(Error::Format("Byte array exceeded max length")); } - if r.remaining() < len.try_into()? { + let len = u64_to_usize(len); + + if r.remaining() < len { return Err(Error::IO("Not enough bytes to fill object")); } - Ok(r.copy_to_bytes(len.try_into()?)) + Ok(r.copy_to_bytes(len)) } fn epee_default_value() -> Option<Self> { @@ -236,11 +241,13 @@ impl EpeeValue for BytesMut { return Err(Error::Format("Byte array exceeded max length")); } - if r.remaining() < len.try_into()? { + let len = u64_to_usize(len); + + if r.remaining() < len { return Err(Error::IO("Not enough bytes to fill object")); } - let mut bytes = BytesMut::zeroed(len.try_into()?); + let mut bytes = BytesMut::zeroed(len); r.copy_to_slice(&mut bytes); Ok(bytes) @@ -272,11 +279,13 @@ impl<const N: usize> EpeeValue for ByteArrayVec<N> { return Err(Error::Format("Byte array exceeded max length")); } - if r.remaining() < usize::try_from(len)? { + let len = u64_to_usize(len); + + if r.remaining() < len { return Err(Error::IO("Not enough bytes to fill object")); } - ByteArrayVec::try_from(r.copy_to_bytes(usize::try_from(len)?)) + ByteArrayVec::try_from(r.copy_to_bytes(len)) .map_err(|_| Error::Format("Field has invalid length")) } @@ -302,7 +311,7 @@ impl<const N: usize> EpeeValue for ByteArray<N> { return Err(Error::Format("Marker does not match expected Marker")); } - let len: usize = read_varint(r)?.try_into()?; + let len = u64_to_usize(read_varint(r)?); if len != N { return Err(Error::Format("Byte array has incorrect length")); } @@ -370,11 +379,11 @@ impl<const N: usize> EpeeValue for Vec<[u8; N]> { )); } - let len = read_varint(r)?; + let len = u64_to_usize(read_varint(r)?); let individual_marker = Marker::new(marker.inner_marker); - let mut res = Vec::with_capacity(len.try_into()?); + let mut res = Vec::with_capacity(len); for _ in 0..len { res.push(<[u8; N]>::read(r, &individual_marker)?); } @@ -406,11 +415,11 @@ macro_rules! epee_seq { )); } - let len = read_varint(r)?; + let len = u64_to_usize(read_varint(r)?); let individual_marker = Marker::new(marker.inner_marker.clone()); - let mut res = Vec::with_capacity(len.try_into()?); + let mut res = Vec::with_capacity(len); for _ in 0..len { res.push(<$val>::read(r, &individual_marker)?); } diff --git a/net/epee-encoding/tests/duplicate_key.rs b/net/epee-encoding/tests/duplicate_key.rs index c1b3148f..0ed87aff 100644 --- a/net/epee-encoding/tests/duplicate_key.rs +++ b/net/epee-encoding/tests/duplicate_key.rs @@ -9,12 +9,12 @@ epee_object!( a: u8, ); -struct TT { +struct T2 { a: u8, } epee_object!( - TT, + T2, a: u8 = 0, ); @@ -35,5 +35,5 @@ fn duplicate_key_with_default() { b'a', 0x0B, 0x00, ]; - assert!(from_bytes::<TT, _>(&mut &data[..]).is_err()); + assert!(from_bytes::<T2, _>(&mut &data[..]).is_err()); } diff --git a/net/levin/Cargo.toml b/net/levin/Cargo.toml index 13deabea..1c585b9c 100644 --- a/net/levin/Cargo.toml +++ b/net/levin/Cargo.toml @@ -12,6 +12,8 @@ default = [] tracing = ["dep:tracing", "tokio-util/tracing"] [dependencies] +cuprate-helper = { path = "../../helper", default-features = false, features = ["cast"] } + thiserror = { workspace = true } bytes = { workspace = true, features = ["std"] } bitflags = { workspace = true } diff --git a/net/levin/src/codec.rs b/net/levin/src/codec.rs index 3718d8c3..1177733f 100644 --- a/net/levin/src/codec.rs +++ b/net/levin/src/codec.rs @@ -20,6 +20,8 @@ use std::{fmt::Debug, marker::PhantomData}; use bytes::{Buf, BufMut, BytesMut}; use tokio_util::codec::{Decoder, Encoder}; +use cuprate_helper::cast::u64_to_usize; + use crate::{ header::{Flags, HEADER_SIZE}, message::{make_dummy_message, LevinMessage}, @@ -114,10 +116,7 @@ impl<C: LevinCommand + Debug> Decoder for LevinBucketCodec<C> { std::mem::replace(&mut self.state, LevinBucketState::WaitingForBody(head)); } LevinBucketState::WaitingForBody(head) => { - let body_len = head - .size - .try_into() - .map_err(|_| BucketError::BucketExceededMaxSize)?; + let body_len = u64_to_usize(head.size); if src.len() < body_len { src.reserve(body_len - src.len()); return Ok(None); @@ -255,13 +254,11 @@ impl<T: LevinBody> Decoder for LevinMessageCodec<T> { continue; }; - let max_size = if self.bucket_codec.handshake_message_seen { + let max_size = u64_to_usize(if self.bucket_codec.handshake_message_seen { self.bucket_codec.protocol.max_packet_size } else { self.bucket_codec.protocol.max_packet_size_before_handshake - } - .try_into() - .expect("Levin max message size is too large, does not fit into a usize."); + }); if bytes.len().saturating_add(bucket.body.len()) > max_size { return Err(BucketError::InvalidFragmentedMessage( @@ -300,12 +297,7 @@ impl<T: LevinBody> Decoder for LevinMessageCodec<T> { } // Check the fragmented message contains enough bytes to build the message. - if bytes.len().saturating_sub(HEADER_SIZE) - < header - .size - .try_into() - .map_err(|_| BucketError::BucketExceededMaxSize)? - { + if bytes.len().saturating_sub(HEADER_SIZE) < u64_to_usize(header.size) { return Err(BucketError::InvalidFragmentedMessage( "Fragmented message does not have enough bytes to fill bucket body", )); diff --git a/net/levin/src/lib.rs b/net/levin/src/lib.rs index 0a247f72..ab03bfb4 100644 --- a/net/levin/src/lib.rs +++ b/net/levin/src/lib.rs @@ -38,6 +38,8 @@ use std::fmt::Debug; use bytes::{Buf, Bytes}; use thiserror::Error; +use cuprate_helper::cast::usize_to_u64; + pub mod codec; pub mod header; pub mod message; @@ -212,7 +214,7 @@ impl<C: LevinCommand> BucketBuilder<C> { Bucket { header: BucketHead { signature: self.signature.unwrap(), - size: body.len().try_into().unwrap(), + size: usize_to_u64(body.len()), have_to_return_data: ty.have_to_return_data(), command: self.command.unwrap(), return_code: self.return_code.unwrap(), diff --git a/net/levin/src/message.rs b/net/levin/src/message.rs index af8227d7..19aa1b50 100644 --- a/net/levin/src/message.rs +++ b/net/levin/src/message.rs @@ -5,6 +5,8 @@ //! for more control over what is actually sent over the wire at certain times. use bytes::{Bytes, BytesMut}; +use cuprate_helper::cast::usize_to_u64; + use crate::{ header::{Flags, HEADER_SIZE}, Bucket, BucketBuilder, BucketError, BucketHead, LevinBody, LevinCommand, Protocol, @@ -106,9 +108,7 @@ pub fn make_fragmented_messages<T: LevinBody>( new_body.resize(fragment_size - HEADER_SIZE, 0); bucket.body = new_body.freeze(); - bucket.header.size = (fragment_size - HEADER_SIZE) - .try_into() - .expect("Bucket size does not fit into u64"); + bucket.header.size = usize_to_u64(fragment_size - HEADER_SIZE); } return Ok(vec![bucket]); @@ -118,9 +118,7 @@ pub fn make_fragmented_messages<T: LevinBody>( // The first fragment will set the START flag, the last will set the END flag. let fragment_head = BucketHead { signature: protocol.signature, - size: (fragment_size - HEADER_SIZE) - .try_into() - .expect("Bucket size does not fit into u64"), + size: usize_to_u64(fragment_size - HEADER_SIZE), have_to_return_data: false, // Just use a default command. command: T::Command::from(0), @@ -191,7 +189,7 @@ pub(crate) fn make_dummy_message<T: LevinCommand>(protocol: &Protocol, size: usi // A header to put on the dummy message. let header = BucketHead { signature: protocol.signature, - size: size.try_into().expect("Bucket size does not fit into u64"), + size: usize_to_u64(size), have_to_return_data: false, // Just use a default command. command: T::from(0), diff --git a/net/levin/tests/fragmented_message.rs b/net/levin/tests/fragmented_message.rs index 7799a719..512fd461 100644 --- a/net/levin/tests/fragmented_message.rs +++ b/net/levin/tests/fragmented_message.rs @@ -8,6 +8,8 @@ use tokio::{ }; use tokio_util::codec::{FramedRead, FramedWrite}; +use cuprate_helper::cast::u64_to_usize; + use cuprate_levin::{ message::make_fragmented_messages, BucketBuilder, BucketError, LevinBody, LevinCommand, LevinMessageCodec, MessageType, Protocol, @@ -54,7 +56,7 @@ impl LevinBody for TestBody { _: MessageType, _: Self::Command, ) -> Result<Self, BucketError> { - let size = body.get_u64_le().try_into().unwrap(); + let size = u64_to_usize(body.get_u64_le()); // bucket Ok(TestBody::Bytes(size, body.copy_to_bytes(size))) } diff --git a/net/wire/Cargo.toml b/net/wire/Cargo.toml index 101daa39..cbeb5511 100644 --- a/net/wire/Cargo.toml +++ b/net/wire/Cargo.toml @@ -15,6 +15,7 @@ cuprate-levin = { path = "../levin" } cuprate-epee-encoding = { path = "../epee-encoding" } cuprate-fixed-bytes = { path = "../fixed-bytes" } cuprate-types = { path = "../../types", default-features = false, features = ["epee"] } +cuprate-helper = { path = "../../helper", default-features = false, features = ["cast"] } bitflags = { workspace = true, features = ["std"] } bytes = { workspace = true, features = ["std"] } diff --git a/net/wire/src/p2p.rs b/net/wire/src/p2p.rs index 97431099..3829d172 100644 --- a/net/wire/src/p2p.rs +++ b/net/wire/src/p2p.rs @@ -99,7 +99,7 @@ impl LevinCommandTrait for LevinCommand { LevinCommand::FluffyMissingTxsRequest => 1024 * 1024, // 1 MB LevinCommand::GetTxPoolCompliment => 1024 * 1024 * 4, // 4 MB - LevinCommand::Unknown(_) => usize::MAX.try_into().unwrap_or(u64::MAX), + LevinCommand::Unknown(_) => u64::MAX, } } diff --git a/rpc/interface/Cargo.toml b/rpc/interface/Cargo.toml index a83c0f07..5f173177 100644 --- a/rpc/interface/Cargo.toml +++ b/rpc/interface/Cargo.toml @@ -20,13 +20,17 @@ cuprate-helper = { path = "../../helper", features = ["asynch"], default- axum = { version = "0.7.5", features = ["json"], default-features = false } serde = { workspace = true, optional = true } -serde_json = { workspace = true, features = ["std"] } tower = { workspace = true } paste = { workspace = true } futures = { workspace = true } [dev-dependencies] +cuprate-test-utils = { path = "../../test-utils" } + axum = { version = "0.7.5", features = ["json", "tokio", "http2"] } serde_json = { workspace = true, features = ["std"] } tokio = { workspace = true, features = ["full"] } -ureq = { version = "2.10.0", features = ["json"] } \ No newline at end of file +ureq = { version = "2.10.0", features = ["json"] } + +[lints] +workspace = true \ No newline at end of file diff --git a/rpc/interface/README.md b/rpc/interface/README.md index 3a63ac46..eb878643 100644 --- a/rpc/interface/README.md +++ b/rpc/interface/README.md @@ -17,7 +17,7 @@ CLIENT ─► ROUTE ─► REQUEST ─► HANDLER ─► RESPONSE ─► CLIENT Everything coming _in_ from a client is handled by this crate. -This is where your [`RpcHandler`] turns this [`RpcRequest`] into a [`RpcResponse`]. +This is where your [`RpcHandler`] turns this `Request` into a `Response`. You hand this `Response` back to `cuprate-rpc-interface` and it will take care of sending it back to the client. @@ -42,16 +42,18 @@ The proper usage of this crate is to: 3. Do whatever with it # The [`RpcHandler`] -This is your [`tower::Service`] that converts [`RpcRequest`]s into [`RpcResponse`]s, +This is your [`tower::Service`] that converts `Request`s into `Response`s, i.e. the "inner handler". -Said concretely, `RpcHandler` is a `tower::Service` where the associated types are from this crate: -- [`RpcRequest`] -- [`RpcResponse`] +Said concretely, `RpcHandler` is 3 `tower::Service`s where the request/response types are +the 3 endpoint enums from [`cuprate_rpc_types`] and the error type is from this crate: +- [`JsonRpcRequest`](cuprate_rpc_types::json::JsonRpcRequest) & [`JsonRpcResponse`](cuprate_rpc_types::json::JsonRpcResponse) +- [`BinRequest`](cuprate_rpc_types::bin::BinRequest) & [`BinResponse`](cuprate_rpc_types::bin::BinRequest) +- [`OtherRequest`](cuprate_rpc_types::other::OtherRequest) & [`OtherResponse`](cuprate_rpc_types::other::OtherRequest) - [`RpcError`] `RpcHandler`'s [`Future`](std::future::Future) is generic, _although_, -it must output `Result<RpcResponse, RpcError>`. +it must output `Result<$RESPONSE, RpcError>`. The `RpcHandler` must also hold some state that is required for RPC server operation. @@ -83,7 +85,7 @@ use cuprate_rpc_types::{ json::{JsonRpcRequest, JsonRpcResponse, GetBlockCountResponse}, other::{OtherRequest, OtherResponse}, }; -use cuprate_rpc_interface::{RouterBuilder, RpcHandlerDummy, RpcRequest}; +use cuprate_rpc_interface::{RouterBuilder, RpcHandlerDummy}; // Send a `/get_height` request. This endpoint has no inputs. async fn get_height(port: u16) -> OtherResponse { diff --git a/rpc/interface/src/lib.rs b/rpc/interface/src/lib.rs index 43bd9e1c..ebea4939 100644 --- a/rpc/interface/src/lib.rs +++ b/rpc/interface/src/lib.rs @@ -1,112 +1,27 @@ #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_cfg))] -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_doc_comments, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style, - unreachable_pub -)] -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // FIXME: good lint but too many false positives - // with our `Env` + `RwLock` setup. - clippy::significant_drop_tightening, - - // FIXME: good lint but is less clear in most cases. - clippy::items_after_statements, - - // TODO - rustdoc::bare_urls, - - clippy::multiple_crate_versions, - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, -)] -// Allow some lints in tests. -#![cfg_attr( - test, - allow( - clippy::cognitive_complexity, - clippy::needless_pass_by_value, - clippy::cast_possible_truncation, - clippy::too_many_lines - ) -)] - -//---------------------------------------------------------------------------------------------------- Mod mod route; mod router_builder; mod rpc_error; mod rpc_handler; #[cfg(feature = "dummy")] mod rpc_handler_dummy; -mod rpc_request; -mod rpc_response; +mod rpc_service; pub use router_builder::RouterBuilder; pub use rpc_error::RpcError; pub use rpc_handler::RpcHandler; #[cfg(feature = "dummy")] pub use rpc_handler_dummy::RpcHandlerDummy; -pub use rpc_request::RpcRequest; -pub use rpc_response::RpcResponse; +pub use rpc_service::RpcService; + +// false-positive: used in `README.md`'s doc-test. +#[cfg(test)] +mod test { + extern crate axum; + extern crate cuprate_test_utils; + extern crate serde_json; + extern crate tokio; + extern crate ureq; +} diff --git a/rpc/interface/src/route/bin.rs b/rpc/interface/src/route/bin.rs index 942e0917..45447caf 100644 --- a/rpc/interface/src/route/bin.rs +++ b/rpc/interface/src/route/bin.rs @@ -7,7 +7,7 @@ use tower::ServiceExt; use cuprate_epee_encoding::from_bytes; use cuprate_rpc_types::bin::{BinRequest, BinResponse, GetTransactionPoolHashesRequest}; -use crate::{rpc_handler::RpcHandler, rpc_request::RpcRequest, rpc_response::RpcResponse}; +use crate::rpc_handler::RpcHandler; //---------------------------------------------------------------------------------------------------- Routes /// This macro generates route functions that expect input. @@ -67,13 +67,8 @@ macro_rules! generate_endpoints_inner { paste::paste! { { // Send request. - let request = RpcRequest::Binary($request); - let channel = $handler.oneshot(request).await?; + let response = $handler.oneshot($request).await?; - // Assert the response from the inner handler is correct. - let RpcResponse::Binary(response) = channel else { - panic!("RPC handler did not return a binary response"); - }; let BinResponse::$variant(response) = response else { panic!("RPC handler returned incorrect response"); }; diff --git a/rpc/interface/src/route/json_rpc.rs b/rpc/interface/src/route/json_rpc.rs index bd35e437..bf3d937d 100644 --- a/rpc/interface/src/route/json_rpc.rs +++ b/rpc/interface/src/route/json_rpc.rs @@ -8,21 +8,21 @@ use tower::ServiceExt; use cuprate_json_rpc::{ error::{ErrorCode, ErrorObject}, - Id, + Id, Response, }; use cuprate_rpc_types::{ json::{JsonRpcRequest, JsonRpcResponse}, RpcCallValue, }; -use crate::{rpc_handler::RpcHandler, rpc_request::RpcRequest, rpc_response::RpcResponse}; +use crate::rpc_handler::RpcHandler; //---------------------------------------------------------------------------------------------------- Routes /// The `/json_rpc` route function used in [`crate::RouterBuilder`]. pub(crate) async fn json_rpc<H: RpcHandler>( State(handler): State<H>, Json(request): Json<cuprate_json_rpc::Request<JsonRpcRequest>>, -) -> Result<Json<cuprate_json_rpc::Response<JsonRpcResponse>>, StatusCode> { +) -> Result<Json<Response<JsonRpcResponse>>, StatusCode> { // TODO: <https://www.jsonrpc.org/specification#notification> // // JSON-RPC notifications (requests without `id`) @@ -30,6 +30,11 @@ pub(crate) async fn json_rpc<H: RpcHandler>( // must remain. How to do this considering this function will // always return and cause `axum` to respond? + // JSON-RPC 2.0 rule: + // If there was an error in detecting the `Request`'s ID, + // the `Response` must contain an `Id::Null` + let id = request.id.unwrap_or(Id::Null); + // Return early if this RPC server is restricted and // the requested method is only for non-restricted RPC. if request.body.is_restricted() && handler.restricted() { @@ -39,26 +44,15 @@ pub(crate) async fn json_rpc<H: RpcHandler>( data: None, }; - // JSON-RPC 2.0 rule: - // If there was an error in detecting the `Request`'s ID, - // the `Response` must contain an `Id::Null` - let id = request.id.unwrap_or(Id::Null); - - let response = cuprate_json_rpc::Response::err(id, error_object); + let response = Response::err(id, error_object); return Ok(Json(response)); } // Send request. - let request = RpcRequest::JsonRpc(request); - let channel = handler.oneshot(request).await?; + let response = handler.oneshot(request.body).await?; - // Assert the response from the inner handler is correct. - let RpcResponse::JsonRpc(response) = channel else { - panic!("RPC handler returned incorrect response"); - }; - - Ok(Json(response)) + Ok(Json(Response::ok(id, response))) } //---------------------------------------------------------------------------------------------------- Tests diff --git a/rpc/interface/src/route/other.rs b/rpc/interface/src/route/other.rs index ce778db9..129ddd59 100644 --- a/rpc/interface/src/route/other.rs +++ b/rpc/interface/src/route/other.rs @@ -25,7 +25,7 @@ use cuprate_rpc_types::{ RpcCall, }; -use crate::{rpc_handler::RpcHandler, rpc_request::RpcRequest, rpc_response::RpcResponse}; +use crate::rpc_handler::RpcHandler; //---------------------------------------------------------------------------------------------------- Routes /// This macro generates route functions that expect input. @@ -81,13 +81,9 @@ macro_rules! generate_endpoints_inner { } // Send request. - let request = RpcRequest::Other(OtherRequest::$variant($request)); - let channel = $handler.oneshot(request).await?; + let request = OtherRequest::$variant($request); + let response = $handler.oneshot(request).await?; - // Assert the response from the inner handler is correct. - let RpcResponse::Other(response) = channel else { - panic!("RPC handler did not return a binary response"); - }; let OtherResponse::$variant(response) = response else { panic!("RPC handler returned incorrect response") }; diff --git a/rpc/interface/src/rpc_handler.rs b/rpc/interface/src/rpc_handler.rs index bcd08733..1299ec48 100644 --- a/rpc/interface/src/rpc_handler.rs +++ b/rpc/interface/src/rpc_handler.rs @@ -1,43 +1,42 @@ //! RPC handler trait. //---------------------------------------------------------------------------------------------------- Use -use std::future::Future; +use cuprate_rpc_types::{ + bin::{BinRequest, BinResponse}, + json::{JsonRpcRequest, JsonRpcResponse}, + other::{OtherRequest, OtherResponse}, +}; -use tower::Service; - -use crate::{rpc_error::RpcError, rpc_request::RpcRequest, rpc_response::RpcResponse}; +use crate::RpcService; //---------------------------------------------------------------------------------------------------- RpcHandler /// An RPC handler. /// -/// This trait represents a type that can turn [`RpcRequest`]s into [`RpcResponse`]s. +/// This trait represents a type that can turn `Request`s into `Response`s. /// -/// Implementors of this trait must be [`tower::Service`]s that use: -/// - [`RpcRequest`] as the generic `Request` type -/// - [`RpcResponse`] as the associated `Response` type -/// - [`RpcError`] as the associated `Error` type -/// - A generic [`Future`] that outputs `Result<RpcResponse, RpcError>` +/// Implementors of this trait must be: +/// - A [`tower::Service`] that uses [`JsonRpcRequest`] & [`JsonRpcResponse`] +/// - A [`tower::Service`] that uses [`BinRequest`] & [`BinResponse`] +/// - A [`tower::Service`] that uses [`OtherRequest`] & [`OtherResponse`] +/// +/// In other words, an [`RpcHandler`] is a type that implements [`tower::Service`] 3 times, +/// one for each request/response enum type found in [`cuprate_rpc_types`]. +/// +/// The error type must always be [`RpcError`](crate::RpcError). /// /// See this crate's `RpcHandlerDummy` for an implementation example of this trait. /// /// # Panics -/// Your [`RpcHandler`] must reply to [`RpcRequest`]s with the correct -/// [`RpcResponse`] or else this crate will panic during routing functions. +/// Your [`RpcHandler`] must reply to `Request`s with the correct +/// `Response` or else this crate will panic during routing functions. /// -/// For example, upon a [`RpcRequest::Binary`] must be replied with -/// [`RpcRequest::Binary`]. If an [`RpcRequest::Other`] were returned instead, -/// this crate would panic. +/// For example, a [`JsonRpcRequest::GetBlockCount`] must be replied with +/// [`JsonRpcResponse::GetBlockCount`]. If anything else is returned, +/// this crate may panic. pub trait RpcHandler: - Clone - + Send - + Sync - + 'static - + Service< - RpcRequest, - Response = RpcResponse, - Error = RpcError, - Future: Future<Output = Result<RpcResponse, RpcError>> + Send + Sync + 'static, - > + RpcService<JsonRpcRequest, JsonRpcResponse> + + RpcService<BinRequest, BinResponse> + + RpcService<OtherRequest, OtherResponse> { /// Is this [`RpcHandler`] restricted? /// diff --git a/rpc/interface/src/rpc_handler_dummy.rs b/rpc/interface/src/rpc_handler_dummy.rs index 73ffe9c8..06fa4608 100644 --- a/rpc/interface/src/rpc_handler_dummy.rs +++ b/rpc/interface/src/rpc_handler_dummy.rs @@ -3,18 +3,19 @@ //---------------------------------------------------------------------------------------------------- Use use std::task::Poll; +use cuprate_rpc_types::{ + bin::{BinRequest, BinResponse}, + json::{JsonRpcRequest, JsonRpcResponse}, + other::{OtherRequest, OtherResponse}, +}; use futures::channel::oneshot::channel; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use tower::Service; use cuprate_helper::asynch::InfallibleOneshotReceiver; -use cuprate_json_rpc::Id; -use crate::{ - rpc_error::RpcError, rpc_handler::RpcHandler, rpc_request::RpcRequest, - rpc_response::RpcResponse, -}; +use crate::{rpc_error::RpcError, rpc_handler::RpcHandler}; //---------------------------------------------------------------------------------------------------- RpcHandlerDummy /// An [`RpcHandler`] that always returns [`Default::default`]. @@ -42,96 +43,133 @@ impl RpcHandler for RpcHandlerDummy { } } -impl Service<RpcRequest> for RpcHandlerDummy { - type Response = RpcResponse; +impl Service<JsonRpcRequest> for RpcHandlerDummy { + type Response = JsonRpcResponse; type Error = RpcError; - type Future = InfallibleOneshotReceiver<Result<RpcResponse, RpcError>>; + type Future = InfallibleOneshotReceiver<Result<JsonRpcResponse, RpcError>>; fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> { Poll::Ready(Ok(())) } - fn call(&mut self, req: RpcRequest) -> Self::Future { - use cuprate_rpc_types::bin::BinRequest as BReq; - use cuprate_rpc_types::bin::BinResponse as BResp; - use cuprate_rpc_types::json::JsonRpcRequest as JReq; - use cuprate_rpc_types::json::JsonRpcResponse as JResp; - use cuprate_rpc_types::other::OtherRequest as OReq; - use cuprate_rpc_types::other::OtherResponse as OResp; + fn call(&mut self, req: JsonRpcRequest) -> Self::Future { + use cuprate_rpc_types::json::JsonRpcRequest as Req; + use cuprate_rpc_types::json::JsonRpcResponse as Resp; - #[rustfmt::skip] #[allow(clippy::default_trait_access)] let resp = match req { - RpcRequest::JsonRpc(j) => RpcResponse::JsonRpc(cuprate_json_rpc::Response::ok(Id::Null, match j.body { - JReq::GetBlockCount(_) => JResp::GetBlockCount(Default::default()), - JReq::OnGetBlockHash(_) => JResp::OnGetBlockHash(Default::default()), - JReq::SubmitBlock(_) => JResp::SubmitBlock(Default::default()), - JReq::GenerateBlocks(_) => JResp::GenerateBlocks(Default::default()), - JReq::GetLastBlockHeader(_) => JResp::GetLastBlockHeader(Default::default()), - JReq::GetBlockHeaderByHash(_) => JResp::GetBlockHeaderByHash(Default::default()), - JReq::GetBlockHeaderByHeight(_) => JResp::GetBlockHeaderByHeight(Default::default()), - JReq::GetBlockHeadersRange(_) => JResp::GetBlockHeadersRange(Default::default()), - JReq::GetBlock(_) => JResp::GetBlock(Default::default()), - JReq::GetConnections(_) => JResp::GetConnections(Default::default()), - JReq::GetInfo(_) => JResp::GetInfo(Default::default()), - JReq::HardForkInfo(_) => JResp::HardForkInfo(Default::default()), - JReq::SetBans(_) => JResp::SetBans(Default::default()), - JReq::GetBans(_) => JResp::GetBans(Default::default()), - JReq::Banned(_) => JResp::Banned(Default::default()), - JReq::FlushTransactionPool(_) => JResp::FlushTransactionPool(Default::default()), - JReq::GetOutputHistogram(_) => JResp::GetOutputHistogram(Default::default()), - JReq::GetCoinbaseTxSum(_) => JResp::GetCoinbaseTxSum(Default::default()), - JReq::GetVersion(_) => JResp::GetVersion(Default::default()), - JReq::GetFeeEstimate(_) => JResp::GetFeeEstimate(Default::default()), - JReq::GetAlternateChains(_) => JResp::GetAlternateChains(Default::default()), - JReq::RelayTx(_) => JResp::RelayTx(Default::default()), - JReq::SyncInfo(_) => JResp::SyncInfo(Default::default()), - JReq::GetTransactionPoolBacklog(_) => JResp::GetTransactionPoolBacklog(Default::default()), - JReq::GetMinerData(_) => JResp::GetMinerData(Default::default()), - JReq::PruneBlockchain(_) => JResp::PruneBlockchain(Default::default()), - JReq::CalcPow(_) => JResp::CalcPow(Default::default()), - JReq::FlushCache(_) => JResp::FlushCache(Default::default()), - JReq::AddAuxPow(_) => JResp::AddAuxPow(Default::default()), - JReq::GetTxIdsLoose(_) => JResp::GetTxIdsLoose(Default::default()), - })), - RpcRequest::Binary(b) => RpcResponse::Binary(match b { - BReq::GetBlocks(_) => BResp::GetBlocks(Default::default()), - BReq::GetBlocksByHeight(_) => BResp::GetBlocksByHeight(Default::default()), - BReq::GetHashes(_) => BResp::GetHashes(Default::default()), - BReq::GetOutputIndexes(_) => BResp::GetOutputIndexes(Default::default()), - BReq::GetOuts(_) => BResp::GetOuts(Default::default()), - BReq::GetTransactionPoolHashes(_) => BResp::GetTransactionPoolHashes(Default::default()), - BReq::GetOutputDistribution(_) => BResp::GetOutputDistribution(Default::default()), - }), - RpcRequest::Other(o) => RpcResponse::Other(match o { - OReq::GetHeight(_) => OResp::GetHeight(Default::default()), - OReq::GetTransactions(_) => OResp::GetTransactions(Default::default()), - OReq::GetAltBlocksHashes(_) => OResp::GetAltBlocksHashes(Default::default()), - OReq::IsKeyImageSpent(_) => OResp::IsKeyImageSpent(Default::default()), - OReq::SendRawTransaction(_) => OResp::SendRawTransaction(Default::default()), - OReq::StartMining(_) => OResp::StartMining(Default::default()), - OReq::StopMining(_) => OResp::StopMining(Default::default()), - OReq::MiningStatus(_) => OResp::MiningStatus(Default::default()), - OReq::SaveBc(_) => OResp::SaveBc(Default::default()), - OReq::GetPeerList(_) => OResp::GetPeerList(Default::default()), - OReq::SetLogHashRate(_) => OResp::SetLogHashRate(Default::default()), - OReq::SetLogLevel(_) => OResp::SetLogLevel(Default::default()), - OReq::SetLogCategories(_) => OResp::SetLogCategories(Default::default()), - OReq::SetBootstrapDaemon(_) => OResp::SetBootstrapDaemon(Default::default()), - OReq::GetTransactionPool(_) => OResp::GetTransactionPool(Default::default()), - OReq::GetTransactionPoolStats(_) => OResp::GetTransactionPoolStats(Default::default()), - OReq::StopDaemon(_) => OResp::StopDaemon(Default::default()), - OReq::GetLimit(_) => OResp::GetLimit(Default::default()), - OReq::SetLimit(_) => OResp::SetLimit(Default::default()), - OReq::OutPeers(_) => OResp::OutPeers(Default::default()), - OReq::InPeers(_) => OResp::InPeers(Default::default()), - OReq::GetNetStats(_) => OResp::GetNetStats(Default::default()), - OReq::GetOuts(_) => OResp::GetOuts(Default::default()), - OReq::Update(_) => OResp::Update(Default::default()), - OReq::PopBlocks(_) => OResp::PopBlocks(Default::default()), - OReq::GetTransactionPoolHashes(_) => OResp::GetTransactionPoolHashes(Default::default()), - OReq::GetPublicNodes(_) => OResp::GetPublicNodes(Default::default()), - }) + Req::GetBlockCount(_) => Resp::GetBlockCount(Default::default()), + Req::OnGetBlockHash(_) => Resp::OnGetBlockHash(Default::default()), + Req::SubmitBlock(_) => Resp::SubmitBlock(Default::default()), + Req::GenerateBlocks(_) => Resp::GenerateBlocks(Default::default()), + Req::GetLastBlockHeader(_) => Resp::GetLastBlockHeader(Default::default()), + Req::GetBlockHeaderByHash(_) => Resp::GetBlockHeaderByHash(Default::default()), + Req::GetBlockHeaderByHeight(_) => Resp::GetBlockHeaderByHeight(Default::default()), + Req::GetBlockHeadersRange(_) => Resp::GetBlockHeadersRange(Default::default()), + Req::GetBlock(_) => Resp::GetBlock(Default::default()), + Req::GetConnections(_) => Resp::GetConnections(Default::default()), + Req::GetInfo(_) => Resp::GetInfo(Default::default()), + Req::HardForkInfo(_) => Resp::HardForkInfo(Default::default()), + Req::SetBans(_) => Resp::SetBans(Default::default()), + Req::GetBans(_) => Resp::GetBans(Default::default()), + Req::Banned(_) => Resp::Banned(Default::default()), + Req::FlushTransactionPool(_) => Resp::FlushTransactionPool(Default::default()), + Req::GetOutputHistogram(_) => Resp::GetOutputHistogram(Default::default()), + Req::GetCoinbaseTxSum(_) => Resp::GetCoinbaseTxSum(Default::default()), + Req::GetVersion(_) => Resp::GetVersion(Default::default()), + Req::GetFeeEstimate(_) => Resp::GetFeeEstimate(Default::default()), + Req::GetAlternateChains(_) => Resp::GetAlternateChains(Default::default()), + Req::RelayTx(_) => Resp::RelayTx(Default::default()), + Req::SyncInfo(_) => Resp::SyncInfo(Default::default()), + Req::GetTransactionPoolBacklog(_) => { + Resp::GetTransactionPoolBacklog(Default::default()) + } + Req::GetMinerData(_) => Resp::GetMinerData(Default::default()), + Req::PruneBlockchain(_) => Resp::PruneBlockchain(Default::default()), + Req::CalcPow(_) => Resp::CalcPow(Default::default()), + Req::FlushCache(_) => Resp::FlushCache(Default::default()), + Req::AddAuxPow(_) => Resp::AddAuxPow(Default::default()), + Req::GetTxIdsLoose(_) => Resp::GetTxIdsLoose(Default::default()), + }; + + let (tx, rx) = channel(); + drop(tx.send(Ok(resp))); + InfallibleOneshotReceiver::from(rx) + } +} + +impl Service<BinRequest> for RpcHandlerDummy { + type Response = BinResponse; + type Error = RpcError; + type Future = InfallibleOneshotReceiver<Result<BinResponse, RpcError>>; + + fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: BinRequest) -> Self::Future { + use cuprate_rpc_types::bin::BinRequest as Req; + use cuprate_rpc_types::bin::BinResponse as Resp; + + #[allow(clippy::default_trait_access)] + let resp = match req { + Req::GetBlocks(_) => Resp::GetBlocks(Default::default()), + Req::GetBlocksByHeight(_) => Resp::GetBlocksByHeight(Default::default()), + Req::GetHashes(_) => Resp::GetHashes(Default::default()), + Req::GetOutputIndexes(_) => Resp::GetOutputIndexes(Default::default()), + Req::GetOuts(_) => Resp::GetOuts(Default::default()), + Req::GetTransactionPoolHashes(_) => Resp::GetTransactionPoolHashes(Default::default()), + Req::GetOutputDistribution(_) => Resp::GetOutputDistribution(Default::default()), + }; + + let (tx, rx) = channel(); + drop(tx.send(Ok(resp))); + InfallibleOneshotReceiver::from(rx) + } +} + +impl Service<OtherRequest> for RpcHandlerDummy { + type Response = OtherResponse; + type Error = RpcError; + type Future = InfallibleOneshotReceiver<Result<OtherResponse, RpcError>>; + + fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: OtherRequest) -> Self::Future { + use cuprate_rpc_types::other::OtherRequest as Req; + use cuprate_rpc_types::other::OtherResponse as Resp; + + #[allow(clippy::default_trait_access)] + let resp = match req { + Req::GetHeight(_) => Resp::GetHeight(Default::default()), + Req::GetTransactions(_) => Resp::GetTransactions(Default::default()), + Req::GetAltBlocksHashes(_) => Resp::GetAltBlocksHashes(Default::default()), + Req::IsKeyImageSpent(_) => Resp::IsKeyImageSpent(Default::default()), + Req::SendRawTransaction(_) => Resp::SendRawTransaction(Default::default()), + Req::StartMining(_) => Resp::StartMining(Default::default()), + Req::StopMining(_) => Resp::StopMining(Default::default()), + Req::MiningStatus(_) => Resp::MiningStatus(Default::default()), + Req::SaveBc(_) => Resp::SaveBc(Default::default()), + Req::GetPeerList(_) => Resp::GetPeerList(Default::default()), + Req::SetLogHashRate(_) => Resp::SetLogHashRate(Default::default()), + Req::SetLogLevel(_) => Resp::SetLogLevel(Default::default()), + Req::SetLogCategories(_) => Resp::SetLogCategories(Default::default()), + Req::SetBootstrapDaemon(_) => Resp::SetBootstrapDaemon(Default::default()), + Req::GetTransactionPool(_) => Resp::GetTransactionPool(Default::default()), + Req::GetTransactionPoolStats(_) => Resp::GetTransactionPoolStats(Default::default()), + Req::StopDaemon(_) => Resp::StopDaemon(Default::default()), + Req::GetLimit(_) => Resp::GetLimit(Default::default()), + Req::SetLimit(_) => Resp::SetLimit(Default::default()), + Req::OutPeers(_) => Resp::OutPeers(Default::default()), + Req::InPeers(_) => Resp::InPeers(Default::default()), + Req::GetNetStats(_) => Resp::GetNetStats(Default::default()), + Req::GetOuts(_) => Resp::GetOuts(Default::default()), + Req::Update(_) => Resp::Update(Default::default()), + Req::PopBlocks(_) => Resp::PopBlocks(Default::default()), + Req::GetTransactionPoolHashes(_) => Resp::GetTransactionPoolHashes(Default::default()), + Req::GetPublicNodes(_) => Resp::GetPublicNodes(Default::default()), }; let (tx, rx) = channel(); diff --git a/rpc/interface/src/rpc_request.rs b/rpc/interface/src/rpc_request.rs deleted file mode 100644 index 3b66a780..00000000 --- a/rpc/interface/src/rpc_request.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! RPC requests. - -//---------------------------------------------------------------------------------------------------- Import -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use cuprate_rpc_types::{bin::BinRequest, json::JsonRpcRequest, other::OtherRequest}; - -//---------------------------------------------------------------------------------------------------- RpcRequest -/// All possible RPC requests. -/// -/// This enum encapsulates all possible RPC requests: -/// - JSON RPC 2.0 requests -/// - Binary requests -/// - Other JSON requests -/// -/// It is the `Request` type required to be used in an [`RpcHandler`](crate::RpcHandler). -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -pub enum RpcRequest { - /// JSON-RPC 2.0 requests. - JsonRpc(cuprate_json_rpc::Request<JsonRpcRequest>), - /// Binary requests. - Binary(BinRequest), - /// Other JSON requests. - Other(OtherRequest), -} - -//---------------------------------------------------------------------------------------------------- Tests -#[cfg(test)] -mod test { - // use super::*; -} diff --git a/rpc/interface/src/rpc_response.rs b/rpc/interface/src/rpc_response.rs deleted file mode 100644 index 7e8ecdbe..00000000 --- a/rpc/interface/src/rpc_response.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! RPC responses. - -//---------------------------------------------------------------------------------------------------- Import -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use cuprate_rpc_types::{bin::BinResponse, json::JsonRpcResponse, other::OtherResponse}; - -//---------------------------------------------------------------------------------------------------- RpcResponse -/// All possible RPC responses. -/// -/// This enum encapsulates all possible RPC responses: -/// - JSON RPC 2.0 responses -/// - Binary responses -/// - Other JSON responses -/// -/// It is the `Response` type required to be used in an [`RpcHandler`](crate::RpcHandler). -#[derive(Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -pub enum RpcResponse { - /// JSON RPC 2.0 responses. - JsonRpc(cuprate_json_rpc::Response<JsonRpcResponse>), - /// Binary responses. - Binary(BinResponse), - /// Other JSON responses. - Other(OtherResponse), -} - -//---------------------------------------------------------------------------------------------------- Tests -#[cfg(test)] -mod test { - // use super::*; -} diff --git a/rpc/interface/src/rpc_service.rs b/rpc/interface/src/rpc_service.rs new file mode 100644 index 00000000..db848307 --- /dev/null +++ b/rpc/interface/src/rpc_service.rs @@ -0,0 +1,52 @@ +//! RPC [`tower::Service`] trait. + +//---------------------------------------------------------------------------------------------------- Use +use std::future::Future; + +use tower::Service; + +use crate::rpc_error::RpcError; + +//---------------------------------------------------------------------------------------------------- RpcService +/// An RPC [`tower::Service`]. +/// +/// This trait solely exists to encapsulate the traits needed +/// to handle RPC requests and respond with responses - **it is +/// not meant to be used directly.** +/// +/// The `Request` and `Response` are generic and +/// are used in the [`tower::Service`] bounds. +/// +/// The error type is always [`RpcError`]. +/// +/// There is a blanket implementation that implements this +/// trait on types that implement `tower::Service` correctly. +/// +/// See [`RpcHandler`](crate::RpcHandler) for more information. +pub trait RpcService<Request, Response>: + Clone + + Send + + Sync + + 'static + + Service< + Request, + Response = Response, + Error = RpcError, + Future: Future<Output = Result<Response, RpcError>> + Send + Sync + 'static, + > +{ +} + +impl<Request, Response, T> RpcService<Request, Response> for T where + Self: Clone + + Send + + Sync + + 'static + + Service< + Request, + Response = Response, + Error = RpcError, + Future: Future<Output = Result<Response, RpcError>> + Send + Sync + 'static, + > +{ +} diff --git a/rpc/json-rpc/Cargo.toml b/rpc/json-rpc/Cargo.toml index 777f3264..5d2544e4 100644 --- a/rpc/json-rpc/Cargo.toml +++ b/rpc/json-rpc/Cargo.toml @@ -17,4 +17,7 @@ serde_json = { workspace = true, features = ["std"] } thiserror = { workspace = true } [dev-dependencies] -pretty_assertions = { workspace = true } \ No newline at end of file +pretty_assertions = { workspace = true } + +[lints] +workspace = true \ No newline at end of file diff --git a/rpc/json-rpc/src/lib.rs b/rpc/json-rpc/src/lib.rs index ce7467a1..dfc4b181 100644 --- a/rpc/json-rpc/src/lib.rs +++ b/rpc/json-rpc/src/lib.rs @@ -1,94 +1,5 @@ #![doc = include_str!("../README.md")] -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - clippy::missing_docs_in_private_items, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // FIXME: good lint but too many false positives - // with our `Env` + `RwLock` setup. - clippy::significant_drop_tightening, - - // FIXME: good lint but is less clear in most cases. - clippy::items_after_statements, - - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, -)] -// Allow some lints in tests. -#![cfg_attr( - test, - allow( - clippy::cognitive_complexity, - clippy::needless_pass_by_value, - clippy::cast_possible_truncation, - clippy::too_many_lines - ) -)] - -//---------------------------------------------------------------------------------------------------- Mod/Use pub mod error; mod id; @@ -103,6 +14,5 @@ pub use request::Request; mod response; pub use response::Response; -//---------------------------------------------------------------------------------------------------- TESTS #[cfg(test)] mod tests; diff --git a/rpc/json-rpc/src/response.rs b/rpc/json-rpc/src/response.rs index efd768b5..2b846069 100644 --- a/rpc/json-rpc/src/response.rs +++ b/rpc/json-rpc/src/response.rs @@ -304,14 +304,14 @@ where if payload.is_none() { payload = Some(Ok(map.next_value::<T>()?)); } else { - return Err(serde::de::Error::duplicate_field("result/error")); + return Err(Error::duplicate_field("result/error")); } } Key::Error => { if payload.is_none() { payload = Some(Err(map.next_value::<ErrorObject>()?)); } else { - return Err(serde::de::Error::duplicate_field("result/error")); + return Err(Error::duplicate_field("result/error")); } } Key::Unknown => { diff --git a/rpc/json-rpc/src/tests.rs b/rpc/json-rpc/src/tests.rs index ff8f0496..3ee60881 100644 --- a/rpc/json-rpc/src/tests.rs +++ b/rpc/json-rpc/src/tests.rs @@ -52,6 +52,7 @@ where } /// Tests an input JSON string matches an expected type `T`. +#[allow(clippy::needless_pass_by_value)] // serde signature fn assert_de<T>(json: &'static str, expected: T) where T: DeserializeOwned + std::fmt::Debug + Clone + PartialEq, diff --git a/rpc/types/Cargo.toml b/rpc/types/Cargo.toml index 9c996818..08b13b18 100644 --- a/rpc/types/Cargo.toml +++ b/rpc/types/Cargo.toml @@ -18,13 +18,14 @@ cuprate-epee-encoding = { path = "../../net/epee-encoding", optional = true } cuprate-fixed-bytes = { path = "../../net/fixed-bytes" } cuprate-types = { path = "../../types" } -monero-serai = { workspace = true } paste = { workspace = true } serde = { workspace = true, optional = true } [dev-dependencies] cuprate-test-utils = { path = "../../test-utils" } -cuprate-json-rpc = { path = "../json-rpc" } -serde_json = { workspace = true } -pretty_assertions = { workspace = true } \ No newline at end of file +serde = { workspace = true } +serde_json = { workspace = true } + +[lints] +workspace = true \ No newline at end of file diff --git a/rpc/types/src/lib.rs b/rpc/types/src/lib.rs index c5f890f6..51ea3cc2 100644 --- a/rpc/types/src/lib.rs +++ b/rpc/types/src/lib.rs @@ -1,96 +1,6 @@ #![doc = include_str!("../README.md")] #![cfg_attr(docsrs, feature(doc_cfg))] -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_doc_comments, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style, - unreachable_pub -)] -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // FIXME: good lint but too many false positives - // with our `Env` + `RwLock` setup. - clippy::significant_drop_tightening, - - // FIXME: good lint but is less clear in most cases. - clippy::items_after_statements, - - clippy::multiple_crate_versions, - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, -)] -// Allow some lints in tests. -#![cfg_attr( - test, - allow( - clippy::cognitive_complexity, - clippy::needless_pass_by_value, - clippy::cast_possible_truncation, - clippy::too_many_lines - ) -)] - -//---------------------------------------------------------------------------------------------------- Mod mod constants; mod defaults; mod free; @@ -112,3 +22,10 @@ pub use constants::{ CORE_RPC_VERSION_MINOR, }; pub use rpc_call::{RpcCall, RpcCallValue}; + +// false-positive: used in tests +#[cfg(test)] +mod test { + extern crate cuprate_test_utils; + extern crate serde_json; +} diff --git a/rpc/types/src/misc/mod.rs b/rpc/types/src/misc/mod.rs index bd6454dd..c5c1840e 100644 --- a/rpc/types/src/misc/mod.rs +++ b/rpc/types/src/misc/mod.rs @@ -15,6 +15,7 @@ mod binary_string; mod distribution; mod key_image_spent_status; +#[allow(clippy::module_inception)] mod misc; mod pool_info_extent; mod status; diff --git a/storage/blockchain/Cargo.toml b/storage/blockchain/Cargo.toml index b03ef038..46b8414d 100644 --- a/storage/blockchain/Cargo.toml +++ b/storage/blockchain/Cargo.toml @@ -40,7 +40,7 @@ thread_local = { workspace = true, optional = true } rayon = { workspace = true, optional = true } [dev-dependencies] -cuprate-helper = { path = "../../helper", features = ["thread"] } +cuprate-helper = { path = "../../helper", features = ["thread", "cast"] } cuprate-test-utils = { path = "../../test-utils" } tokio = { workspace = true, features = ["full"] } @@ -49,3 +49,6 @@ pretty_assertions = { workspace = true } proptest = { workspace = true } hex = { workspace = true } hex-literal = { workspace = true } + +[lints] +workspace = true diff --git a/storage/blockchain/src/config/config.rs b/storage/blockchain/src/config.rs similarity index 82% rename from storage/blockchain/src/config/config.rs rename to storage/blockchain/src/config.rs index 957c67c0..e4b76068 100644 --- a/storage/blockchain/src/config/config.rs +++ b/storage/blockchain/src/config.rs @@ -1,4 +1,44 @@ -//! The main [`Config`] struct, holding all configurable values. +//! Database configuration. +//! +//! This module contains the main [`Config`]uration struct +//! for the database [`Env`](cuprate_database::Env)ironment, +//! and blockchain-specific configuration. +//! +//! It also contains types related to configuration settings. +//! +//! The main constructor is the [`ConfigBuilder`]. +//! +//! These configurations are processed at runtime, meaning +//! the `Env` can/will dynamically adjust its behavior based +//! on these values. +//! +//! # Example +//! ```rust +//! use cuprate_blockchain::{ +//! cuprate_database::{Env, config::SyncMode}, +//! config::{ConfigBuilder, ReaderThreads}, +//! }; +//! +//! # fn main() -> Result<(), Box<dyn std::error::Error>> { +//! let tmp_dir = tempfile::tempdir()?; +//! let db_dir = tmp_dir.path().to_owned(); +//! +//! let config = ConfigBuilder::new() +//! // Use a custom database directory. +//! .db_directory(db_dir.into()) +//! // Use as many reader threads as possible (when using `service`). +//! .reader_threads(ReaderThreads::OnePerThread) +//! // Use the fastest sync mode. +//! .sync_mode(SyncMode::Fast) +//! // Build into `Config` +//! .build(); +//! +//! // Start a database `service` using this configuration. +//! let (_, _, env) = cuprate_blockchain::service::init(config.clone())?; +//! // It's using the config we provided. +//! assert_eq!(env.config(), &config.db_config); +//! # Ok(()) } +//! ``` //---------------------------------------------------------------------------------------------------- Import use std::{borrow::Cow, path::Path}; diff --git a/storage/blockchain/src/config/mod.rs b/storage/blockchain/src/config/mod.rs deleted file mode 100644 index 555a6e6e..00000000 --- a/storage/blockchain/src/config/mod.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! Database configuration. -//! -//! This module contains the main [`Config`]uration struct -//! for the database [`Env`](cuprate_database::Env)ironment, -//! and blockchain-specific configuration. -//! -//! It also contains types related to configuration settings. -//! -//! The main constructor is the [`ConfigBuilder`]. -//! -//! These configurations are processed at runtime, meaning -//! the `Env` can/will dynamically adjust its behavior based -//! on these values. -//! -//! # Example -//! ```rust -//! use cuprate_blockchain::{ -//! cuprate_database::{Env, config::SyncMode}, -//! config::{ConfigBuilder, ReaderThreads}, -//! }; -//! -//! # fn main() -> Result<(), Box<dyn std::error::Error>> { -//! let tmp_dir = tempfile::tempdir()?; -//! let db_dir = tmp_dir.path().to_owned(); -//! -//! let config = ConfigBuilder::new() -//! // Use a custom database directory. -//! .db_directory(db_dir.into()) -//! // Use as many reader threads as possible (when using `service`). -//! .reader_threads(ReaderThreads::OnePerThread) -//! // Use the fastest sync mode. -//! .sync_mode(SyncMode::Fast) -//! // Build into `Config` -//! .build(); -//! -//! // Start a database `service` using this configuration. -//! let (_, _, env) = cuprate_blockchain::service::init(config.clone())?; -//! // It's using the config we provided. -//! assert_eq!(env.config(), &config.db_config); -//! # Ok(()) } -//! ``` - -mod config; -pub use config::{Config, ConfigBuilder, ReaderThreads}; diff --git a/storage/blockchain/src/lib.rs b/storage/blockchain/src/lib.rs index 8a6f96b6..f66cd99b 100644 --- a/storage/blockchain/src/lib.rs +++ b/storage/blockchain/src/lib.rs @@ -1,104 +1,8 @@ #![doc = include_str!("../README.md")] -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - clippy::missing_docs_in_private_items, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_crate_dependencies, - unused_doc_comments, - unused_mut, - //missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] #![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // FIXME: good lint but too many false positives - // with our `Env` + `RwLock` setup. - clippy::significant_drop_tightening, - - // FIXME: good lint but is less clear in most cases. - clippy::items_after_statements, - - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, + // See `cuprate-database` for reasoning. + clippy::significant_drop_tightening )] -// Allow some lints when running in debug mode. -#![cfg_attr( - debug_assertions, - allow( - clippy::todo, - clippy::multiple_crate_versions, - // unused_crate_dependencies, - ) -)] -// Allow some lints in tests. -#![cfg_attr( - test, - allow( - clippy::cognitive_complexity, - clippy::needless_pass_by_value, - clippy::cast_possible_truncation, - clippy::too_many_lines - ) -)] -extern crate core; // Only allow building 64-bit targets. // diff --git a/storage/blockchain/src/ops/block.rs b/storage/blockchain/src/ops/block.rs index 5cb3b4bc..229d35e1 100644 --- a/storage/blockchain/src/ops/block.rs +++ b/storage/blockchain/src/ops/block.rs @@ -475,7 +475,7 @@ mod test { let mut block = BLOCK_V9_TX3.clone(); - block.height = usize::try_from(u32::MAX).unwrap() + 1; + block.height = cuprate_helper::cast::u32_to_usize(u32::MAX) + 1; add_block(&block, &mut tables).unwrap(); } diff --git a/storage/blockchain/src/service/free.rs b/storage/blockchain/src/service/free.rs index aa8238f9..7a4e6ce6 100644 --- a/storage/blockchain/src/service/free.rs +++ b/storage/blockchain/src/service/free.rs @@ -37,8 +37,8 @@ pub fn init( let db = Arc::new(crate::open(config)?); // Spawn the Reader thread pool and Writer. - let readers = init_read_service(db.clone(), reader_threads); - let writer = init_write_service(db.clone()); + let readers = init_read_service(Arc::clone(&db), reader_threads); + let writer = init_write_service(Arc::clone(&db)); Ok((readers, writer, db)) } diff --git a/storage/blockchain/src/service/tests.rs b/storage/blockchain/src/service/tests.rs index ed13f7b3..b68b5444 100644 --- a/storage/blockchain/src/service/tests.rs +++ b/storage/blockchain/src/service/tests.rs @@ -304,8 +304,9 @@ async fn test_template( // Assert we get back the same map of // `Amount`'s and `AmountIndex`'s. let mut response_output_count = 0; + #[allow(clippy::iter_over_hash_type)] // order doesn't matter in this test for (amount, output_map) in response { - let amount_index_set = map.get(&amount).unwrap(); + let amount_index_set = &map[&amount]; for (amount_index, output) in output_map { response_output_count += 1; diff --git a/storage/database/Cargo.toml b/storage/database/Cargo.toml index a70457f5..0ef4a97d 100644 --- a/storage/database/Cargo.toml +++ b/storage/database/Cargo.toml @@ -32,4 +32,7 @@ serde = { workspace = true, optional = true } [dev-dependencies] bytemuck = { version = "1.14.3", features = ["must_cast", "derive", "min_const_generics", "extern_crate_alloc"] } page_size = { version = "0.6.0" } -tempfile = { version = "3.10.0" } \ No newline at end of file +tempfile = { version = "3.10.0" } + +[lints] +workspace = true \ No newline at end of file diff --git a/storage/database/src/backend/heed/env.rs b/storage/database/src/backend/heed/env.rs index 0c2847fb..8c71e617 100644 --- a/storage/database/src/backend/heed/env.rs +++ b/storage/database/src/backend/heed/env.rs @@ -70,7 +70,7 @@ impl Drop for ConcreteEnv { // We need to do `mdb_env_set_flags(&env, MDB_NOSYNC|MDB_ASYNCMAP, 0)` // to clear the no sync and async flags such that the below `self.sync()` // _actually_ synchronously syncs. - if let Err(_e) = crate::Env::sync(self) { + if let Err(_e) = Env::sync(self) { // TODO: log error? } diff --git a/storage/database/src/backend/heed/storable.rs b/storage/database/src/backend/heed/storable.rs index 3566e88f..da0e0cb5 100644 --- a/storage/database/src/backend/heed/storable.rs +++ b/storage/database/src/backend/heed/storable.rs @@ -78,8 +78,8 @@ mod test { println!("left: {left:?}, right: {right:?}, expected: {expected:?}"); assert_eq!( <StorableHeed::<T> as heed::Comparator>::compare( - &<StorableHeed::<T> as heed::BytesEncode>::bytes_encode(&left).unwrap(), - &<StorableHeed::<T> as heed::BytesEncode>::bytes_encode(&right).unwrap() + &<StorableHeed::<T> as BytesEncode>::bytes_encode(&left).unwrap(), + &<StorableHeed::<T> as BytesEncode>::bytes_encode(&right).unwrap() ), expected ); diff --git a/storage/database/src/backend/redb/database.rs b/storage/database/src/backend/redb/database.rs index cd9a0be9..dafb2417 100644 --- a/storage/database/src/backend/redb/database.rs +++ b/storage/database/src/backend/redb/database.rs @@ -23,7 +23,7 @@ use crate::{ /// Shared [`DatabaseRo::get()`]. #[inline] fn get<T: Table + 'static>( - db: &impl redb::ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, + db: &impl ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, key: &T::Key, ) -> Result<T::Value, RuntimeError> { Ok(db.get(key)?.ok_or(RuntimeError::KeyNotFound)?.value()) @@ -32,7 +32,7 @@ fn get<T: Table + 'static>( /// Shared [`DatabaseRo::len()`]. #[inline] fn len<T: Table>( - db: &impl redb::ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, + db: &impl ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, ) -> Result<u64, RuntimeError> { Ok(db.len()?) } @@ -40,7 +40,7 @@ fn len<T: Table>( /// Shared [`DatabaseRo::first()`]. #[inline] fn first<T: Table>( - db: &impl redb::ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, + db: &impl ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, ) -> Result<(T::Key, T::Value), RuntimeError> { let (key, value) = db.first()?.ok_or(RuntimeError::KeyNotFound)?; Ok((key.value(), value.value())) @@ -49,7 +49,7 @@ fn first<T: Table>( /// Shared [`DatabaseRo::last()`]. #[inline] fn last<T: Table>( - db: &impl redb::ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, + db: &impl ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, ) -> Result<(T::Key, T::Value), RuntimeError> { let (key, value) = db.last()?.ok_or(RuntimeError::KeyNotFound)?; Ok((key.value(), value.value())) @@ -58,7 +58,7 @@ fn last<T: Table>( /// Shared [`DatabaseRo::is_empty()`]. #[inline] fn is_empty<T: Table>( - db: &impl redb::ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, + db: &impl ReadableTable<StorableRedb<T::Key>, StorableRedb<T::Value>>, ) -> Result<bool, RuntimeError> { Ok(db.is_empty()?) } diff --git a/storage/database/src/config/mod.rs b/storage/database/src/config/mod.rs index 19a324e1..c6ed0c01 100644 --- a/storage/database/src/config/mod.rs +++ b/storage/database/src/config/mod.rs @@ -33,6 +33,7 @@ //! # Ok(()) } //! ``` +#[allow(clippy::module_inception)] mod config; pub use config::{Config, ConfigBuilder, READER_THREADS_DEFAULT}; diff --git a/storage/database/src/env.rs b/storage/database/src/env.rs index cae49733..82944434 100644 --- a/storage/database/src/env.rs +++ b/storage/database/src/env.rs @@ -163,7 +163,7 @@ pub trait Env: Sized { // We have the direct PATH to the file, // no need to use backend-specific functions. // - // SAFETY: as we are only accessing the metadata of + // INVARIANT: as we are only accessing the metadata of // the file and not reading the bytes, it should be // fine even with a memory mapped file being actively // written to. diff --git a/storage/database/src/key.rs b/storage/database/src/key.rs index 3273d4ed..2f3855a4 100644 --- a/storage/database/src/key.rs +++ b/storage/database/src/key.rs @@ -163,11 +163,11 @@ impl KeyCompare { #[inline] pub const fn as_compare_fn<K: Key>(self) -> fn(&[u8], &[u8]) -> Ordering { match self { - Self::Default => std::cmp::Ord::cmp, + Self::Default => Ord::cmp, Self::Number => |left, right| { let left = <K as Storable>::from_bytes(left); let right = <K as Storable>::from_bytes(right); - std::cmp::Ord::cmp(&left, &right) + Ord::cmp(&left, &right) }, Self::Custom(f) => f, } diff --git a/storage/database/src/lib.rs b/storage/database/src/lib.rs index 5946fe5e..45bfc53c 100644 --- a/storage/database/src/lib.rs +++ b/storage/database/src/lib.rs @@ -1,94 +1,18 @@ #![doc = include_str!("../README.md")] -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - while_true, - clippy::missing_docs_in_private_items, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - single_use_lifetimes, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_crate_dependencies, - unused_doc_comments, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] #![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - // FIXME: good lint but too many false positives - // with our `Env` + `RwLock` setup. - clippy::significant_drop_tightening, - - // FIXME: good lint but is less clear in most cases. - clippy::items_after_statements, - - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, - - // unused_crate_dependencies, // false-positive with `paste` -)] -// Allow some lints when running in debug mode. -#![cfg_attr( - debug_assertions, - allow( - clippy::todo, - clippy::multiple_crate_versions, - // unused_crate_dependencies, - ) + // This lint is allowed because the following + // code exists a lot in this crate: + // + // ```rust + // let env_inner = env.env_inner(); + // let tx_rw = env_inner.tx_rw()?; + // OpenTables::create_tables(&env_inner, &tx_rw)?; + // ``` + // + // Rust thinks `env_inner` can be dropped earlier + // but it cannot, we need it for the lifetime of + // the database transaction + tables. + clippy::significant_drop_tightening )] // Allow some lints in tests. #![cfg_attr( diff --git a/storage/txpool/Cargo.toml b/storage/txpool/Cargo.toml index d5ea77d0..70211d9e 100644 --- a/storage/txpool/Cargo.toml +++ b/storage/txpool/Cargo.toml @@ -41,3 +41,6 @@ cuprate-test-utils = { path = "../../test-utils" } tokio = { workspace = true } tempfile = { workspace = true } hex-literal = { workspace = true } + +[lints] +workspace = true \ No newline at end of file diff --git a/storage/txpool/src/config.rs b/storage/txpool/src/config.rs index 8d09b5e5..1ef0d734 100644 --- a/storage/txpool/src/config.rs +++ b/storage/txpool/src/config.rs @@ -211,7 +211,7 @@ impl Config { /// assert_eq!(config.reader_threads, ReaderThreads::default()); /// ``` pub fn new() -> Self { - Config { + Self { db_config: DbConfig::new(Cow::Borrowed(&*CUPRATE_TXPOOL_DIR)), reader_threads: ReaderThreads::default(), max_txpool_weight: 0, diff --git a/storage/txpool/src/lib.rs b/storage/txpool/src/lib.rs index f200c348..243dc4d9 100644 --- a/storage/txpool/src/lib.rs +++ b/storage/txpool/src/lib.rs @@ -1,4 +1,8 @@ #![doc = include_str!("../README.md")] +#![allow( + // See `cuprate-database` for reasoning. + clippy::significant_drop_tightening +)] pub mod config; mod free; @@ -13,3 +17,13 @@ pub use free::open; //re-exports pub use cuprate_database; + +// TODO: remove when used. +use tower as _; +#[cfg(test)] +mod test { + use cuprate_test_utils as _; + use hex_literal as _; + use tempfile as _; + use tokio as _; +} diff --git a/storage/txpool/src/ops/key_images.rs b/storage/txpool/src/ops/key_images.rs index c6e44152..04aa1b44 100644 --- a/storage/txpool/src/ops/key_images.rs +++ b/storage/txpool/src/ops/key_images.rs @@ -11,7 +11,7 @@ use crate::{ops::TxPoolWriteError, tables::SpentKeyImages, types::TransactionHas /// /// # Panics /// This function will panic if any of the [`Input`]s are not [`Input::ToKey`] -pub fn add_tx_key_images( +pub(super) fn add_tx_key_images( inputs: &[Input], tx_hash: &TransactionHash, kis_table: &mut impl DatabaseRw<SpentKeyImages>, @@ -31,7 +31,7 @@ pub fn add_tx_key_images( /// /// # Panics /// This function will panic if any of the [`Input`]s are not [`Input::ToKey`] -pub fn remove_tx_key_images( +pub(super) fn remove_tx_key_images( inputs: &[Input], kis_table: &mut impl DatabaseRw<SpentKeyImages>, ) -> Result<(), RuntimeError> { diff --git a/storage/txpool/src/service/free.rs b/storage/txpool/src/service/free.rs index 614ab5c4..003da552 100644 --- a/storage/txpool/src/service/free.rs +++ b/storage/txpool/src/service/free.rs @@ -30,8 +30,8 @@ pub fn init( let db = Arc::new(crate::open(config)?); // Spawn the Reader thread pool and Writer. - let readers = init_read_service(db.clone(), reader_threads); - let writer = init_write_service(db.clone()); + let readers = init_read_service(Arc::clone(&db), reader_threads); + let writer = init_write_service(Arc::clone(&db)); Ok((readers, writer, db)) } diff --git a/storage/txpool/src/service/read.rs b/storage/txpool/src/service/read.rs index c2fee66d..56541641 100644 --- a/storage/txpool/src/service/read.rs +++ b/storage/txpool/src/service/read.rs @@ -25,7 +25,7 @@ use crate::{ /// Should be called _once_ per actual database. #[cold] #[inline(never)] // Only called once. -pub fn init_read_service(env: Arc<ConcreteEnv>, threads: ReaderThreads) -> TxpoolReadHandle { +pub(super) fn init_read_service(env: Arc<ConcreteEnv>, threads: ReaderThreads) -> TxpoolReadHandle { init_read_service_with_pool(env, init_thread_pool(threads)) } @@ -35,10 +35,7 @@ pub fn init_read_service(env: Arc<ConcreteEnv>, threads: ReaderThreads) -> Txpoo /// Should be called _once_ per actual database. #[cold] #[inline(never)] // Only called once. -pub fn init_read_service_with_pool( - env: Arc<ConcreteEnv>, - pool: Arc<ThreadPool>, -) -> TxpoolReadHandle { +fn init_read_service_with_pool(env: Arc<ConcreteEnv>, pool: Arc<ThreadPool>) -> TxpoolReadHandle { DatabaseReadService::new(env, pool, map_request) } @@ -53,6 +50,7 @@ pub fn init_read_service_with_pool( /// 1. `Request` is mapped to a handler function /// 2. Handler function is called /// 3. [`TxpoolReadResponse`] is returned +#[allow(clippy::needless_pass_by_value)] fn map_request( env: &ConcreteEnv, // Access to the database request: TxpoolReadRequest, // The request we must fulfill diff --git a/storage/txpool/src/service/write.rs b/storage/txpool/src/service/write.rs index f6bdb385..8a3b1bf7 100644 --- a/storage/txpool/src/service/write.rs +++ b/storage/txpool/src/service/write.rs @@ -16,7 +16,7 @@ use crate::{ //---------------------------------------------------------------------------------------------------- init_write_service /// Initialize the txpool write service from a [`ConcreteEnv`]. -pub fn init_write_service(env: Arc<ConcreteEnv>) -> TxpoolWriteHandle { +pub(super) fn init_write_service(env: Arc<ConcreteEnv>) -> TxpoolWriteHandle { DatabaseWriteHandle::init(env, handle_txpool_request) } diff --git a/storage/txpool/src/types.rs b/storage/txpool/src/types.rs index 5c89d3b9..09b0ce0d 100644 --- a/storage/txpool/src/types.rs +++ b/storage/txpool/src/types.rs @@ -35,10 +35,11 @@ bitflags::bitflags! { pub struct TransactionInfo { /// The transaction's fee. pub fee: u64, - /// The transaction`s weight. + /// The transaction's weight. pub weight: usize, /// [`TxStateFlags`] of this transaction. pub flags: TxStateFlags, + #[allow(clippy::pub_underscore_fields)] /// Explicit padding so that we have no implicit padding bytes in `repr(C)`. /// /// Allows potential future expansion of this type. @@ -68,21 +69,21 @@ impl From<RawCachedVerificationState> for CachedVerificationState { fn from(value: RawCachedVerificationState) -> Self { // if the hash is all `0`s then there is no hash this is valid at. if value.raw_valid_at_hash == [0; 32] { - return CachedVerificationState::NotVerified; + return Self::NotVerified; } let raw_valid_past_timestamp = u64::from_le_bytes(value.raw_valid_past_timestamp); // if the timestamp is 0, there is no timestamp that needs to be passed. if raw_valid_past_timestamp == 0 { - return CachedVerificationState::ValidAtHashAndHF { + return Self::ValidAtHashAndHF { block_hash: value.raw_valid_at_hash, hf: HardFork::from_version(value.raw_hf) .expect("hard-fork values stored in the DB should always be valid"), }; } - CachedVerificationState::ValidAtHashAndHFWithTimeBasedLock { + Self::ValidAtHashAndHFWithTimeBasedLock { block_hash: value.raw_valid_at_hash, hf: HardFork::from_version(value.raw_hf) .expect("hard-fork values stored in the DB should always be valid"), @@ -91,6 +92,7 @@ impl From<RawCachedVerificationState> for CachedVerificationState { } } +#[allow(clippy::fallible_impl_from)] // only panics in invalid states impl From<CachedVerificationState> for RawCachedVerificationState { fn from(value: CachedVerificationState) -> Self { match value { diff --git a/types/Cargo.toml b/types/Cargo.toml index 4c31cfc0..4b9204b9 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -23,10 +23,12 @@ bytes = { workspace = true } curve25519-dalek = { workspace = true } monero-serai = { workspace = true } serde = { workspace = true, features = ["derive"], optional = true } -borsh = { workspace = true, optional = true } thiserror = { workspace = true } proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } -[dev-dependencies] \ No newline at end of file +[dev-dependencies] + +[lints] +workspace = true \ No newline at end of file diff --git a/types/src/lib.rs b/types/src/lib.rs index d70f4c31..0b0dbe67 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -1,76 +1,6 @@ #![doc = include_str!("../README.md")] -//---------------------------------------------------------------------------------------------------- Lints -// Forbid lints. -// Our code, and code generated (e.g macros) cannot overrule these. -#![forbid( - // `unsafe` is allowed but it _must_ be - // commented with `SAFETY: reason`. - clippy::undocumented_unsafe_blocks, - - // Never. - unused_unsafe, - redundant_semicolons, - unused_allocation, - coherence_leak_check, - single_use_lifetimes, - while_true, - clippy::missing_docs_in_private_items, - - // Maybe can be put into `#[deny]`. - unconditional_recursion, - for_loops_over_fallibles, - unused_braces, - unused_doc_comments, - unused_labels, - keyword_idents, - non_ascii_idents, - variant_size_differences, - - // Probably can be put into `#[deny]`. - future_incompatible, - let_underscore, - break_with_label_and_loop, - duplicate_macro_attributes, - exported_private_dependencies, - large_assignments, - overlapping_range_endpoints, - semicolon_in_expressions_from_macros, - noop_method_call, - unreachable_pub, -)] -// Deny lints. -// Some of these are `#[allow]`'ed on a per-case basis. -#![deny( - clippy::all, - clippy::correctness, - clippy::suspicious, - clippy::style, - clippy::complexity, - clippy::perf, - clippy::pedantic, - clippy::nursery, - clippy::cargo, - unused_mut, - missing_docs, - deprecated, - unused_comparisons, - nonstandard_style -)] -#![allow( - // FIXME: this lint affects crates outside of - // `database/` for some reason, allow for now. - clippy::cargo_common_metadata, - - // FIXME: adding `#[must_use]` onto everything - // might just be more annoying than useful... - // although it is sometimes nice. - clippy::must_use_candidate, - - clippy::module_name_repetitions, - clippy::module_inception, - clippy::redundant_pub_crate, - clippy::option_if_let_else, -)] +// `proptest` needs this internally. +#![cfg_attr(any(feature = "proptest"), allow(non_local_definitions))] // Allow some lints when running in debug mode. #![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] @@ -97,4 +27,5 @@ pub use types::{ //---------------------------------------------------------------------------------------------------- Feature-gated #[cfg(feature = "blockchain")] pub mod blockchain; + //---------------------------------------------------------------------------------------------------- Private