From d43f12ea3f525b221b586896a382b9373bb30207 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Fri, 11 Oct 2024 13:51:16 -0400 Subject: [PATCH 1/6] rpc-types: fix `HardForkInfoRequest` (#310) * apply diffs * fix tests --- rpc/types/src/json.rs | 13 +++++++++++-- test-utils/src/rpc/data/json.rs | 5 ++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/rpc/types/src/json.rs b/rpc/types/src/json.rs index d3426b46c..fd9ffa32b 100644 --- a/rpc/types/src/json.rs +++ b/rpc/types/src/json.rs @@ -817,8 +817,17 @@ define_request_and_response! { hard_fork_info, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1958..=1995, - HardForkInfo (empty), - Request {}, + HardForkInfo, + + #[doc = serde_doc_test!( + HARD_FORK_INFO_REQUEST => HardForkInfoRequest { + version: 16, + } + )] + #[derive(Copy)] + Request { + version: u8, + }, #[doc = serde_doc_test!( HARD_FORK_INFO_RESPONSE => HardForkInfoResponse { diff --git a/test-utils/src/rpc/data/json.rs b/test-utils/src/rpc/data/json.rs index a05af6700..3d4630623 100644 --- a/test-utils/src/rpc/data/json.rs +++ b/test-utils/src/rpc/data/json.rs @@ -608,7 +608,10 @@ define_request_and_response! { r#"{ "jsonrpc": "2.0", "id": "0", - "method": "hard_fork_info" + "method": "hard_fork_info", + "params": { + "version": 16 + } }"#; Response = r#"{ From f9b847b2272f4e7a57172028d295acb4e13c225e Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Fri, 11 Oct 2024 18:57:43 -0400 Subject: [PATCH 2/6] types: `HardFork` improvements (#309) * apply diffs * review fixes --- Cargo.lock | 23 ++++++++ Cargo.toml | 1 + types/Cargo.toml | 1 + types/src/hard_fork.rs | 128 +++++++++++++++++++++++++++++++---------- 4 files changed, 122 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fea18d04..1b05efadb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -933,6 +933,7 @@ dependencies = [ "proptest-derive", "serde", "serde_json", + "strum", "thiserror", ] @@ -2673,6 +2674,28 @@ dependencies = [ "spin", ] +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.77", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index fa348ccdc..6c322fbd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ rayon = { version = "1.10.0", default-features = false } serde_bytes = { version = "0.11.15", default-features = false } serde_json = { version = "1.0.128", default-features = false } serde = { version = "1.0.210", default-features = false } +strum = { version = "0.26.3", default-features = false } thiserror = { version = "1.0.63", default-features = false } thread_local = { version = "1.1.8", default-features = false } tokio-util = { version = "0.7.12", default-features = false } diff --git a/types/Cargo.toml b/types/Cargo.toml index 1c7629028..8ac6b25f7 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -27,6 +27,7 @@ curve25519-dalek = { workspace = true } monero-serai = { workspace = true } hex = { workspace = true, features = ["serde", "alloc"], optional = true } serde = { workspace = true, features = ["derive"], optional = true } +strum = { workspace = true, features = ["derive"] } thiserror = { workspace = true } proptest = { workspace = true, optional = true } diff --git a/types/src/hard_fork.rs b/types/src/hard_fork.rs index 8b2cd78cc..d16032f57 100644 --- a/types/src/hard_fork.rs +++ b/types/src/hard_fork.rs @@ -1,6 +1,10 @@ //! The [`HardFork`] type. use std::time::Duration; +use strum::{ + AsRefStr, Display, EnumCount, EnumIs, EnumString, FromRepr, IntoStaticStr, VariantArray, +}; + use monero_serai::block::BlockHeader; /// Target block time for hf 1. @@ -27,7 +31,25 @@ pub enum HardForkError { } /// An identifier for every hard-fork Monero has had. -#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)] +#[derive( + Default, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Copy, + Clone, + Hash, + EnumCount, + Display, + AsRefStr, + EnumIs, + EnumString, + FromRepr, + IntoStaticStr, + VariantArray, +)] #[cfg_attr(any(feature = "proptest"), derive(proptest_derive::Arbitrary))] #[repr(u8)] pub enum HardFork { @@ -47,58 +69,75 @@ pub enum HardFork { V13, V14, V15, - // remember to update from_vote! V16, } impl HardFork { + /// The latest [`HardFork`]. + /// + /// ```rust + /// # use cuprate_types::HardFork; + /// assert_eq!(HardFork::LATEST, HardFork::V16); + /// ``` + pub const LATEST: Self = Self::VARIANTS[Self::COUNT - 1]; + /// Returns the hard-fork for a blocks [`BlockHeader::hardfork_version`] field. /// /// ref: /// /// # Errors - /// /// Will return [`Err`] if the version is not a valid [`HardFork`]. + /// + /// ```rust + /// # use cuprate_types::{HardFork, HardForkError}; + /// # use strum::VariantArray; + /// assert_eq!(HardFork::from_version(0), Err(HardForkError::HardForkUnknown)); + /// assert_eq!(HardFork::from_version(17), Err(HardForkError::HardForkUnknown)); + /// + /// for (version, hf) in HardFork::VARIANTS.iter().enumerate() { + /// // +1 because enumerate starts at 0, hf starts at 1. + /// assert_eq!(*hf, HardFork::from_version(version as u8 + 1).unwrap()); + /// } + /// ``` #[inline] pub const fn from_version(version: u8) -> Result { - Ok(match version { - 1 => Self::V1, - 2 => Self::V2, - 3 => Self::V3, - 4 => Self::V4, - 5 => Self::V5, - 6 => Self::V6, - 7 => Self::V7, - 8 => Self::V8, - 9 => Self::V9, - 10 => Self::V10, - 11 => Self::V11, - 12 => Self::V12, - 13 => Self::V13, - 14 => Self::V14, - 15 => Self::V15, - 16 => Self::V16, - _ => return Err(HardForkError::HardForkUnknown), - }) + match Self::from_repr(version) { + Some(this) => Ok(this), + None => Err(HardForkError::HardForkUnknown), + } } /// Returns the hard-fork for a blocks [`BlockHeader::hardfork_signal`] (vote) field. /// /// + /// + /// ```rust + /// # use cuprate_types::{HardFork, HardForkError}; + /// # use strum::VariantArray; + /// // 0 is interpreted as 1. + /// assert_eq!(HardFork::from_vote(0), HardFork::V1); + /// // Unknown defaults to `LATEST`. + /// assert_eq!(HardFork::from_vote(17), HardFork::V16); + /// + /// for (vote, hf) in HardFork::VARIANTS.iter().enumerate() { + /// // +1 because enumerate starts at 0, hf starts at 1. + /// assert_eq!(*hf, HardFork::from_vote(vote as u8 + 1)); + /// } + /// ``` #[inline] pub fn from_vote(vote: u8) -> Self { if vote == 0 { // A vote of 0 is interpreted as 1 as that's what Monero used to default to. - return Self::V1; + Self::V1 + } else { + // This must default to the latest hard-fork! + Self::from_version(vote).unwrap_or(Self::LATEST) } - // This must default to the latest hard-fork! - Self::from_version(vote).unwrap_or(Self::V16) } /// Returns the [`HardFork`] version and vote from this block header. /// /// # Errors - /// /// Will return [`Err`] if the [`BlockHeader::hardfork_version`] is not a valid [`HardFork`]. #[inline] pub fn from_block_header(header: &BlockHeader) -> Result<(Self, Self), HardForkError> { @@ -109,22 +148,49 @@ impl HardFork { } /// Returns the raw hard-fork value, as it would appear in [`BlockHeader::hardfork_version`]. - pub const fn as_u8(&self) -> u8 { - *self as u8 + /// + /// ```rust + /// # use cuprate_types::{HardFork, HardForkError}; + /// # use strum::VariantArray; + /// for (i, hf) in HardFork::VARIANTS.iter().enumerate() { + /// // +1 because enumerate starts at 0, hf starts at 1. + /// assert_eq!(hf.as_u8(), i as u8 + 1); + /// } + /// ``` + pub const fn as_u8(self) -> u8 { + self as u8 } /// Returns the next hard-fork. - pub fn next_fork(&self) -> Option { - Self::from_version(*self as u8 + 1).ok() + pub fn next_fork(self) -> Option { + Self::from_version(self as u8 + 1).ok() } /// Returns the target block time for this hardfork. /// /// ref: - pub const fn block_time(&self) -> Duration { + pub const fn block_time(self) -> Duration { match self { Self::V1 => BLOCK_TIME_V1, _ => BLOCK_TIME_V2, } } + + /// Returns `true` if `self` is [`Self::LATEST`]. + /// + /// ```rust + /// # use cuprate_types::HardFork; + /// # use strum::VariantArray; + /// + /// for hf in HardFork::VARIANTS.iter() { + /// if *hf == HardFork::LATEST { + /// assert!(hf.is_latest()); + /// } else { + /// assert!(!hf.is_latest()); + /// } + /// } + /// ``` + pub const fn is_latest(self) -> bool { + matches!(self, Self::LATEST) + } } From 978d72b6c14f27bd61836d90f304d766cc7a93f2 Mon Sep 17 00:00:00 2001 From: SyntheticBird <118022351+SyntheticBird45@users.noreply.github.com> Date: Wed, 16 Oct 2024 23:17:58 +0000 Subject: [PATCH 3/6] Move consensus context service into a subcrate. (#318) Co-authored-by: Boog900 --- Cargo.lock | 23 +++++- Cargo.toml | 3 +- binaries/cuprated/Cargo.toml | 1 + binaries/cuprated/src/blockchain/manager.rs | 7 +- .../src/blockchain/manager/handler.rs | 8 +-- .../src/rpc/request/blockchain_context.rs | 2 +- books/architecture/src/appendix/crates.md | 3 +- consensus/Cargo.toml | 6 +- consensus/context/Cargo.toml | 24 +++++++ .../context => context/src}/alt_chains.rs | 13 ++-- .../context => context/src}/difficulty.rs | 23 +++--- .../{src/context => context/src}/hardforks.rs | 28 ++++---- .../{src/context.rs => context/src/lib.rs} | 71 ++++++++++++++++--- .../{src/context => context/src}/rx_vms.rs | 32 ++++----- .../{src/context => context/src}/task.rs | 13 ++-- .../{src/context => context/src}/tokens.rs | 0 .../{src/context => context/src}/weight.rs | 16 ++--- consensus/fast-sync/Cargo.toml | 13 ++-- consensus/fast-sync/src/fast_sync.rs | 6 +- consensus/src/block.rs | 4 +- consensus/src/block/alt_block.rs | 12 ++-- consensus/src/block/batch_prepare.rs | 2 +- consensus/src/lib.rs | 3 +- consensus/src/tests/context.rs | 12 ++-- consensus/src/tests/context/difficulty.rs | 2 +- consensus/src/tests/context/hardforks.rs | 10 ++- consensus/src/tests/context/rx_vms.rs | 6 +- consensus/src/tests/context/weight.rs | 8 +-- 28 files changed, 218 insertions(+), 133 deletions(-) create mode 100644 consensus/context/Cargo.toml rename consensus/{src/context => context/src}/alt_chains.rs (94%) rename consensus/{src/context => context/src}/difficulty.rs (95%) rename consensus/{src/context => context/src}/hardforks.rs (90%) rename consensus/{src/context.rs => context/src/lib.rs} (88%) rename consensus/{src/context => context/src}/rx_vms.rs (90%) rename consensus/{src/context => context/src}/task.rs (96%) rename consensus/{src/context => context/src}/tokens.rs (100%) rename consensus/{src/context => context/src}/weight.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index 1b05efadb..ca0174ba6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,6 +575,7 @@ name = "cuprate-consensus" version = "0.1.0" dependencies = [ "cfg-if", + "cuprate-consensus-context", "cuprate-consensus-rules", "cuprate-helper", "cuprate-test-utils", @@ -587,12 +588,30 @@ dependencies = [ "proptest", "proptest-derive", "rand", - "randomx-rs", "rayon", "thiserror", "thread_local", "tokio", "tokio-test", + "tower 0.5.1", + "tracing", +] + +[[package]] +name = "cuprate-consensus-context" +version = "0.1.0" +dependencies = [ + "cuprate-consensus-rules", + "cuprate-helper", + "cuprate-types", + "futures", + "hex", + "monero-serai", + "randomx-rs", + "rayon", + "thiserror", + "thread_local", + "tokio", "tokio-util", "tower 0.5.1", "tracing", @@ -704,6 +723,7 @@ dependencies = [ "clap", "cuprate-blockchain", "cuprate-consensus", + "cuprate-consensus-context", "cuprate-consensus-rules", "cuprate-helper", "cuprate-types", @@ -972,6 +992,7 @@ dependencies = [ "cuprate-async-buffer", "cuprate-blockchain", "cuprate-consensus", + "cuprate-consensus-context", "cuprate-consensus-rules", "cuprate-cryptonight", "cuprate-dandelion-tower", diff --git a/Cargo.toml b/Cargo.toml index 6c322fbd8..2ef99d62d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "binaries/cuprated", "constants", "consensus", + "consensus/context", "consensus/fast-sync", "consensus/rules", "cryptonight", @@ -322,4 +323,4 @@ non_camel_case_types = "deny" # unused_results = "deny" # non_exhaustive_omitted_patterns = "deny" # missing_docs = "deny" -# missing_copy_implementations = "deny" \ No newline at end of file +# missing_copy_implementations = "deny" diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index 325406bf7..59fa9784e 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/binaries/cuprated" # TODO: after v1.0.0, remove unneeded dependencies. cuprate-consensus = { path = "../../consensus" } cuprate-fast-sync = { path = "../../consensus/fast-sync" } +cuprate-consensus-context = { path = "../../consensus/context" } cuprate-consensus-rules = { path = "../../consensus/rules" } cuprate-cryptonight = { path = "../../cryptonight" } cuprate-helper = { path = "../../helper" } diff --git a/binaries/cuprated/src/blockchain/manager.rs b/binaries/cuprated/src/blockchain/manager.rs index 118c8dd62..8e613bc98 100644 --- a/binaries/cuprated/src/blockchain/manager.rs +++ b/binaries/cuprated/src/blockchain/manager.rs @@ -8,10 +8,11 @@ use tracing::error; use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle}; use cuprate_consensus::{ - context::RawBlockChainContext, BlockChainContextRequest, BlockChainContextResponse, - BlockChainContextService, BlockVerifierService, ExtendedConsensusError, TxVerifierService, - VerifyBlockRequest, VerifyBlockResponse, VerifyTxRequest, VerifyTxResponse, + BlockChainContextRequest, BlockChainContextResponse, BlockChainContextService, + BlockVerifierService, ExtendedConsensusError, TxVerifierService, VerifyBlockRequest, + VerifyBlockResponse, VerifyTxRequest, VerifyTxResponse, }; +use cuprate_consensus_context::RawBlockChainContext; use cuprate_p2p::{ block_downloader::{BlockBatch, BlockDownloaderConfig}, BroadcastSvc, NetworkInterface, diff --git a/binaries/cuprated/src/blockchain/manager/handler.rs b/binaries/cuprated/src/blockchain/manager/handler.rs index 9603bad54..e9805cd7a 100644 --- a/binaries/cuprated/src/blockchain/manager/handler.rs +++ b/binaries/cuprated/src/blockchain/manager/handler.rs @@ -10,11 +10,11 @@ use tracing::info; use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle}; use cuprate_consensus::{ - block::PreparedBlock, context::NewBlockData, transactions::new_tx_verification_data, - BlockChainContextRequest, BlockChainContextResponse, BlockVerifierService, - ExtendedConsensusError, VerifyBlockRequest, VerifyBlockResponse, VerifyTxRequest, - VerifyTxResponse, + block::PreparedBlock, transactions::new_tx_verification_data, BlockChainContextRequest, + BlockChainContextResponse, BlockVerifierService, ExtendedConsensusError, VerifyBlockRequest, + VerifyBlockResponse, VerifyTxRequest, VerifyTxResponse, }; +use cuprate_consensus_context::NewBlockData; use cuprate_helper::cast::usize_to_u64; use cuprate_p2p::{block_downloader::BlockBatch, constants::LONG_BAN, BroadcastRequest}; use cuprate_types::{ diff --git a/binaries/cuprated/src/rpc/request/blockchain_context.rs b/binaries/cuprated/src/rpc/request/blockchain_context.rs index b616593d4..2b14d467f 100644 --- a/binaries/cuprated/src/rpc/request/blockchain_context.rs +++ b/binaries/cuprated/src/rpc/request/blockchain_context.rs @@ -5,7 +5,7 @@ use std::convert::Infallible; use anyhow::Error; use tower::{Service, ServiceExt}; -use cuprate_consensus::context::{ +use cuprate_consensus_context::{ BlockChainContext, BlockChainContextRequest, BlockChainContextResponse, BlockChainContextService, }; diff --git a/books/architecture/src/appendix/crates.md b/books/architecture/src/appendix/crates.md index 1993c47e7..fe8f1f05b 100644 --- a/books/architecture/src/appendix/crates.md +++ b/books/architecture/src/appendix/crates.md @@ -16,7 +16,8 @@ cargo doc --open --package cuprate-blockchain | Crate | In-tree path | Purpose | |-------|--------------|---------| | [`cuprate-consensus`](https://doc.cuprate.org/cuprate_consensus) | [`consensus/`](https://github.com/Cuprate/cuprate/tree/main/consensus) | TODO -| [`cuprate-consensus-rules`](https://doc.cuprate.org/cuprate_consensus_rules) | [`consensus/rules/`](https://github.com/Cuprate/cuprate/tree/main/consensus-rules) | TODO +| [`cuprate-consensus-context`](https://doc.cuprate.org/cuprate_consensus_context) | [`consensus/context/`](https://github.com/Cuprate/cuprate/tree/main/consensus/context) | TODO +| [`cuprate-consensus-rules`](https://doc.cuprate.org/cuprate_consensus_rules) | [`consensus/rules/`](https://github.com/Cuprate/cuprate/tree/main/consensus/rules) | TODO | [`cuprate-fast-sync`](https://doc.cuprate.org/cuprate_fast_sync) | [`consensus/fast-sync/`](https://github.com/Cuprate/cuprate/tree/main/consensus/fast-sync) | Fast block synchronization ## Networking diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index 12d97eed3..1fdee89a7 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/consensus" cuprate-helper = { path = "../helper", default-features = false, features = ["std", "asynch", "num"] } cuprate-consensus-rules = { path = "./rules", features = ["rayon"] } cuprate-types = { path = "../types" } +cuprate-consensus-context = { path = "./context" } cfg-if = { workspace = true } thiserror = { workspace = true } @@ -18,13 +19,10 @@ tower = { workspace = true, features = ["util"] } tracing = { workspace = true, features = ["std", "attributes"] } futures = { workspace = true, features = ["std", "async-await"] } -randomx-rs = { workspace = true } monero-serai = { workspace = true, features = ["std"] } rayon = { workspace = true } thread_local = { workspace = true } -tokio = { workspace = true, features = ["rt"] } -tokio-util = { workspace = true } hex = { workspace = true } rand = { workspace = true } @@ -42,4 +40,4 @@ proptest = { workspace = true } proptest-derive = { workspace = true } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/consensus/context/Cargo.toml b/consensus/context/Cargo.toml new file mode 100644 index 000000000..00804204d --- /dev/null +++ b/consensus/context/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cuprate-consensus-context" +version = "0.1.0" +edition = "2021" +license = "MIT" +authors = ["SyntheticBird","Boog900"] + +[dependencies] +cuprate-consensus-rules = { path = "../rules", features = ["proptest"]} +cuprate-helper = { path = "../../helper", default-features = false, features = ["std", "cast"] } +cuprate-types = { path = "../../types", default-features = false } + +futures = { workspace = true, features = ["std", "async-await"] } +tokio = { workspace = true, features = ["rt-multi-thread", "macros"]} +tokio-util = { workspace = true } +tower = { workspace = true, features = ["util"] } +tracing = { workspace = true, features = ["std", "attributes"] } +thiserror = { workspace = true } + +monero-serai = { workspace = true, features = ["std"] } +randomx-rs = { workspace = true } +rayon = { workspace = true } +thread_local = { workspace = true } +hex = { workspace = true } diff --git a/consensus/src/context/alt_chains.rs b/consensus/context/src/alt_chains.rs similarity index 94% rename from consensus/src/context/alt_chains.rs rename to consensus/context/src/alt_chains.rs index cd945c819..df82ef342 100644 --- a/consensus/src/context/alt_chains.rs +++ b/consensus/context/src/alt_chains.rs @@ -9,9 +9,8 @@ use cuprate_types::{ }; use crate::{ - ExtendedConsensusError, - __private::Database, - context::{difficulty::DifficultyCache, rx_vms::RandomXVm, weight::BlockWeightsCache}, + ContextCacheError, __private::Database, difficulty::DifficultyCache, rx_vms::RandomXVm, + weight::BlockWeightsCache, }; pub(crate) mod sealed { @@ -38,7 +37,7 @@ pub struct AltChainContextCache { pub chain_height: usize, /// The top hash of the alt chain. pub top_hash: [u8; 32], - /// The [`ChainID`] of the alt chain. + /// The [`ChainId`] of the alt chain. pub chain_id: Option, /// The parent [`Chain`] of this alt chain. pub parent_chain: Chain, @@ -98,7 +97,7 @@ impl AltChainMap { &mut self, prev_id: [u8; 32], database: D, - ) -> Result, ExtendedConsensusError> { + ) -> Result, ContextCacheError> { if let Some(cache) = self.alt_cache_map.remove(&prev_id) { return Ok(cache); } @@ -133,7 +132,7 @@ pub(crate) async fn get_alt_chain_difficulty_cache( prev_id: [u8; 32], main_chain_difficulty_cache: &DifficultyCache, mut database: D, -) -> Result { +) -> Result { // find the block with hash == prev_id. let BlockchainResponse::FindBlock(res) = database .ready() @@ -180,7 +179,7 @@ pub(crate) async fn get_alt_chain_weight_cache( prev_id: [u8; 32], main_chain_weight_cache: &BlockWeightsCache, mut database: D, -) -> Result { +) -> Result { // find the block with hash == prev_id. let BlockchainResponse::FindBlock(res) = database .ready() diff --git a/consensus/src/context/difficulty.rs b/consensus/context/src/difficulty.rs similarity index 95% rename from consensus/src/context/difficulty.rs rename to consensus/context/src/difficulty.rs index 9316dc5ee..e3f558a01 100644 --- a/consensus/src/context/difficulty.rs +++ b/consensus/context/src/difficulty.rs @@ -17,7 +17,7 @@ use cuprate_types::{ Chain, }; -use crate::{Database, ExtendedConsensusError, HardFork}; +use crate::{ContextCacheError, Database, HardFork}; /// The amount of blocks we account for to calculate difficulty const DIFFICULTY_WINDOW: usize = 720; @@ -33,9 +33,9 @@ const DIFFICULTY_LAG: usize = 15; /// #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct DifficultyCacheConfig { - pub(crate) window: usize, - pub(crate) cut: usize, - pub(crate) lag: usize, + pub window: usize, + pub cut: usize, + pub lag: usize, } impl DifficultyCacheConfig { @@ -73,14 +73,13 @@ impl DifficultyCacheConfig { #[derive(Debug, Clone, Eq, PartialEq)] pub struct DifficultyCache { /// The list of timestamps in the window. - /// len <= [`DIFFICULTY_BLOCKS_COUNT`] - pub(crate) timestamps: VecDeque, + pub timestamps: VecDeque, /// The current cumulative difficulty of the chain. - pub(crate) cumulative_difficulties: VecDeque, + pub cumulative_difficulties: VecDeque, /// The last height we accounted for. - pub(crate) last_accounted_height: usize, + pub last_accounted_height: usize, /// The config - pub(crate) config: DifficultyCacheConfig, + pub config: DifficultyCacheConfig, } impl DifficultyCache { @@ -91,7 +90,7 @@ impl DifficultyCache { config: DifficultyCacheConfig, database: D, chain: Chain, - ) -> Result { + ) -> Result { tracing::info!("Initializing difficulty cache this may take a while."); let mut block_start = chain_height.saturating_sub(config.total_block_count()); @@ -134,7 +133,7 @@ impl DifficultyCache { &mut self, numb_blocks: usize, database: D, - ) -> Result<(), ExtendedConsensusError> { + ) -> Result<(), ContextCacheError> { let Some(retained_blocks) = self.timestamps.len().checked_sub(numb_blocks) else { // More blocks to pop than we have in the cache, so just restart a new cache. *self = Self::init_from_chain_height( @@ -361,7 +360,7 @@ async fn get_blocks_in_pow_info( database: D, block_heights: Range, chain: Chain, -) -> Result<(VecDeque, VecDeque), ExtendedConsensusError> { +) -> Result<(VecDeque, VecDeque), ContextCacheError> { tracing::info!("Getting blocks timestamps"); let BlockchainResponse::BlockExtendedHeaderInRange(ext_header) = database diff --git a/consensus/src/context/hardforks.rs b/consensus/context/src/hardforks.rs similarity index 90% rename from consensus/src/context/hardforks.rs rename to consensus/context/src/hardforks.rs index 16ae76384..e6af492b6 100644 --- a/consensus/src/context/hardforks.rs +++ b/consensus/context/src/hardforks.rs @@ -9,7 +9,7 @@ use cuprate_types::{ Chain, }; -use crate::{Database, ExtendedConsensusError}; +use crate::{ContextCacheError, Database}; /// The default amount of hard-fork votes to track to decide on activation of a hard-fork. /// @@ -21,9 +21,9 @@ const DEFAULT_WINDOW_SIZE: usize = 10080; // supermajority window check length - #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct HardForkConfig { /// The network we are on. - pub(crate) info: HFsInfo, + pub info: HFsInfo, /// The amount of votes we are taking into account to decide on a fork activation. - pub(crate) window: usize, + pub window: usize, } impl HardForkConfig { @@ -54,17 +54,17 @@ impl HardForkConfig { /// A struct that keeps track of the current hard-fork and current votes. #[derive(Debug, Clone, Eq, PartialEq)] -pub(crate) struct HardForkState { +pub struct HardForkState { /// The current active hard-fork. - pub(crate) current_hardfork: HardFork, + pub current_hardfork: HardFork, /// The hard-fork config. - pub(crate) config: HardForkConfig, + pub config: HardForkConfig, /// The votes in the current window. - pub(crate) votes: HFVotes, + pub votes: HFVotes, /// The last block height accounted for. - pub(crate) last_height: usize, + pub last_height: usize, } impl HardForkState { @@ -74,7 +74,7 @@ impl HardForkState { chain_height: usize, config: HardForkConfig, mut database: D, - ) -> Result { + ) -> Result { tracing::info!("Initializing hard-fork state this may take a while."); let block_start = chain_height.saturating_sub(config.window); @@ -122,11 +122,11 @@ impl HardForkState { /// # Invariant /// /// This _must_ only be used on a main-chain cache. - pub(crate) async fn pop_blocks_main_chain( + pub async fn pop_blocks_main_chain( &mut self, numb_blocks: usize, database: D, - ) -> Result<(), ExtendedConsensusError> { + ) -> Result<(), ContextCacheError> { let Some(retained_blocks) = self.votes.total_votes().checked_sub(self.config.window) else { *self = Self::init_from_chain_height( self.last_height + 1 - numb_blocks, @@ -159,7 +159,7 @@ impl HardForkState { } /// Add a new block to the cache. - pub(crate) fn new_block(&mut self, vote: HardFork, height: usize) { + pub fn new_block(&mut self, vote: HardFork, height: usize) { // We don't _need_ to take in `height` but it's for safety, so we don't silently loose track // of blocks. assert_eq!(self.last_height + 1, height); @@ -194,7 +194,7 @@ impl HardForkState { } /// Returns the current hard-fork. - pub(crate) const fn current_hardfork(&self) -> HardFork { + pub const fn current_hardfork(&self) -> HardFork { self.current_hardfork } } @@ -205,7 +205,7 @@ async fn get_votes_in_range( database: D, block_heights: Range, window_size: usize, -) -> Result { +) -> Result { let mut votes = HFVotes::new(window_size); let BlockchainResponse::BlockExtendedHeaderInRange(vote_list) = database diff --git a/consensus/src/context.rs b/consensus/context/src/lib.rs similarity index 88% rename from consensus/src/context.rs rename to consensus/context/src/lib.rs index 3c944a926..82e601d7b 100644 --- a/consensus/src/context.rs +++ b/consensus/context/src/lib.rs @@ -1,6 +1,6 @@ //! # Blockchain Context //! -//! This module contains a service to get cached context from the blockchain: [`BlockChainContext`]. +//! This crate contains a service to get cached context from the blockchain: [`BlockChainContext`]. //! This is used during contextual validation, this does not have all the data for contextual validation //! (outputs) for that you will need a [`Database`]. //! @@ -18,14 +18,14 @@ use tokio::sync::mpsc; use tokio_util::sync::PollSender; use tower::Service; -use cuprate_consensus_rules::{blocks::ContextToVerifyBlock, current_unix_timestamp, HardFork}; - -use crate::{Database, ExtendedConsensusError}; +use cuprate_consensus_rules::{ + blocks::ContextToVerifyBlock, current_unix_timestamp, ConsensusError, HardFork, +}; -pub(crate) mod difficulty; -pub(crate) mod hardforks; -pub(crate) mod rx_vms; -pub(crate) mod weight; +pub mod difficulty; +pub mod hardforks; +pub mod rx_vms; +pub mod weight; mod alt_chains; mod task; @@ -36,13 +36,13 @@ use difficulty::DifficultyCache; use rx_vms::RandomXVm; use weight::BlockWeightsCache; -pub(crate) use alt_chains::{sealed::AltChainRequestToken, AltChainContextCache}; +pub use alt_chains::{sealed::AltChainRequestToken, AltChainContextCache}; pub use difficulty::DifficultyCacheConfig; pub use hardforks::HardForkConfig; pub use tokens::*; pub use weight::BlockWeightsCacheConfig; -pub(crate) const BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW: u64 = 60; +pub const BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW: u64 = 60; /// Config for the context service. pub struct ContextConfig { @@ -91,7 +91,7 @@ impl ContextConfig { pub async fn initialize_blockchain_context( cfg: ContextConfig, database: D, -) -> Result +) -> Result where D: Database + Clone + Send + Sync + 'static, D::Future: Send + 'static, @@ -414,3 +414,52 @@ impl Service for BlockChainContextService { .boxed() } } + +#[derive(Debug, thiserror::Error)] +pub enum ContextCacheError { + /// A consensus error. + #[error("{0}")] + ConErr(#[from] ConsensusError), + /// A database error. + #[error("Database error: {0}")] + DBErr(#[from] tower::BoxError), +} + +use __private::Database; + +pub mod __private { + use std::future::Future; + + use cuprate_types::blockchain::{BlockchainReadRequest, BlockchainResponse}; + + /// A type alias trait used to represent a database, so we don't have to write [`tower::Service`] bounds + /// everywhere. + /// + /// Automatically implemented for: + /// ```ignore + /// tower::Service + /// ``` + pub trait Database: + tower::Service< + BlockchainReadRequest, + Response = BlockchainResponse, + Error = tower::BoxError, + Future = Self::Future2, + > + { + type Future2: Future> + Send + 'static; + } + + impl< + T: tower::Service< + BlockchainReadRequest, + Response = BlockchainResponse, + Error = tower::BoxError, + >, + > Database for T + where + T::Future: Future> + Send + 'static, + { + type Future2 = T::Future; + } +} diff --git a/consensus/src/context/rx_vms.rs b/consensus/context/src/rx_vms.rs similarity index 90% rename from consensus/src/context/rx_vms.rs rename to consensus/context/src/rx_vms.rs index c6375fc13..803bb324e 100644 --- a/consensus/src/context/rx_vms.rs +++ b/consensus/context/src/rx_vms.rs @@ -26,10 +26,10 @@ use cuprate_types::{ Chain, }; -use crate::{Database, ExtendedConsensusError}; +use crate::{ContextCacheError, Database}; /// The amount of randomX VMs to keep in the cache. -const RX_SEEDS_CACHED: usize = 2; +pub const RX_SEEDS_CACHED: usize = 2; /// A multithreaded randomX VM. #[derive(Debug)] @@ -72,14 +72,14 @@ impl RandomX for RandomXVm { /// The randomX VMs cache, keeps the VM needed to calculate the current block's proof-of-work hash (if a VM is needed) and a /// couple more around this VM. #[derive(Clone, Debug)] -pub(crate) struct RandomXVmCache { +pub struct RandomXVmCache { /// The top [`RX_SEEDS_CACHED`] RX seeds. - pub(crate) seeds: VecDeque<(usize, [u8; 32])>, + pub seeds: VecDeque<(usize, [u8; 32])>, /// The VMs for `seeds` (if after hf 12, otherwise this will be empty). - pub(crate) vms: HashMap>, + pub vms: HashMap>, /// A single cached VM that was given to us from a part of Cuprate. - pub(crate) cached_vm: Option<([u8; 32], Arc)>, + pub cached_vm: Option<([u8; 32], Arc)>, } impl RandomXVmCache { @@ -88,7 +88,7 @@ impl RandomXVmCache { chain_height: usize, hf: &HardFork, database: D, - ) -> Result { + ) -> Result { let seed_heights = get_last_rx_seed_heights(chain_height - 1, RX_SEEDS_CACHED); let seed_hashes = get_block_hashes(seed_heights.clone(), database).await?; @@ -125,18 +125,18 @@ impl RandomXVmCache { } /// Add a randomX VM to the cache, with the seed it was created with. - pub(crate) fn add_vm(&mut self, vm: ([u8; 32], Arc)) { + pub fn add_vm(&mut self, vm: ([u8; 32], Arc)) { self.cached_vm.replace(vm); } /// Creates a RX VM for an alt chain, looking at the main chain RX VMs to see if we can use one /// of them first. - pub(crate) async fn get_alt_vm( + pub async fn get_alt_vm( &self, height: usize, chain: Chain, database: D, - ) -> Result, ExtendedConsensusError> { + ) -> Result, ContextCacheError> { let seed_height = randomx_seed_height(height); let BlockchainResponse::BlockHash(seed_hash) = database @@ -162,7 +162,7 @@ impl RandomXVmCache { } /// Get the main-chain `RandomX` VMs. - pub(crate) async fn get_vms(&mut self) -> HashMap> { + pub async fn get_vms(&mut self) -> HashMap> { match self.seeds.len().checked_sub(self.vms.len()) { // No difference in the amount of seeds to VMs. Some(0) => (), @@ -214,7 +214,7 @@ impl RandomXVmCache { } /// Removes all the `RandomX` VMs above the `new_height`. - pub(crate) fn pop_blocks_main_chain(&mut self, new_height: usize) { + pub fn pop_blocks_main_chain(&mut self, new_height: usize) { self.seeds.retain(|(height, _)| *height < new_height); self.vms.retain(|height, _| *height < new_height); } @@ -222,7 +222,7 @@ impl RandomXVmCache { /// Add a new block to the VM cache. /// /// hash is the block hash not the blocks proof-of-work hash. - pub(crate) fn new_block(&mut self, height: usize, hash: &[u8; 32]) { + pub fn new_block(&mut self, height: usize, hash: &[u8; 32]) { if is_randomx_seed_height(height) { tracing::debug!("Block {height} is a randomX seed height, adding it to the cache.",); @@ -243,7 +243,7 @@ impl RandomXVmCache { /// Get the last `amount` of RX seeds, the top height returned here will not necessarily be the RX VM for the top block /// in the chain as VMs include some lag before a seed activates. -pub(crate) fn get_last_rx_seed_heights(mut last_height: usize, mut amount: usize) -> Vec { +pub fn get_last_rx_seed_heights(mut last_height: usize, mut amount: usize) -> Vec { let mut seeds = Vec::with_capacity(amount); if is_randomx_seed_height(last_height) { seeds.push(last_height); @@ -268,7 +268,7 @@ pub(crate) fn get_last_rx_seed_heights(mut last_height: usize, mut amount: usize async fn get_block_hashes( heights: Vec, database: D, -) -> Result, ExtendedConsensusError> { +) -> Result, ContextCacheError> { let mut fut = FuturesOrdered::new(); for height in heights { @@ -281,7 +281,7 @@ async fn get_block_hashes( else { panic!("Database sent incorrect response!"); }; - Result::<_, ExtendedConsensusError>::Ok(hash) + Result::<_, ContextCacheError>::Ok(hash) }); } diff --git a/consensus/src/context/task.rs b/consensus/context/src/task.rs similarity index 96% rename from consensus/src/context/task.rs rename to consensus/context/src/task.rs index c51c795ea..65cfea99c 100644 --- a/consensus/src/context/task.rs +++ b/consensus/context/src/task.rs @@ -16,13 +16,10 @@ use cuprate_types::{ }; use crate::{ - context::{ - alt_chains::{get_alt_chain_difficulty_cache, get_alt_chain_weight_cache, AltChainMap}, - difficulty, hardforks, rx_vms, weight, BlockChainContext, BlockChainContextRequest, - BlockChainContextResponse, ContextConfig, RawBlockChainContext, ValidityToken, - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW, - }, - Database, ExtendedConsensusError, + alt_chains::{get_alt_chain_difficulty_cache, get_alt_chain_weight_cache, AltChainMap}, + difficulty, hardforks, rx_vms, weight, BlockChainContext, BlockChainContextRequest, + BlockChainContextResponse, ContextCacheError, ContextConfig, Database, RawBlockChainContext, + ValidityToken, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW, }; /// A request from the context service to the context task. @@ -68,7 +65,7 @@ impl ContextTask { pub(crate) async fn init_context( cfg: ContextConfig, mut database: D, - ) -> Result { + ) -> Result { let ContextConfig { difficulty_cfg, weights_config, diff --git a/consensus/src/context/tokens.rs b/consensus/context/src/tokens.rs similarity index 100% rename from consensus/src/context/tokens.rs rename to consensus/context/src/tokens.rs diff --git a/consensus/src/context/weight.rs b/consensus/context/src/weight.rs similarity index 96% rename from consensus/src/context/weight.rs rename to consensus/context/src/weight.rs index e95ae6056..7f725998a 100644 --- a/consensus/src/context/weight.rs +++ b/consensus/context/src/weight.rs @@ -21,12 +21,12 @@ use cuprate_types::{ Chain, }; -use crate::{Database, ExtendedConsensusError, HardFork}; +use crate::{ContextCacheError, Database, HardFork}; /// The short term block weight window. -const SHORT_TERM_WINDOW: usize = 100; +pub const SHORT_TERM_WINDOW: usize = 100; /// The long term block weight window. -const LONG_TERM_WINDOW: usize = 100000; +pub const LONG_TERM_WINDOW: usize = 100000; /// Configuration for the block weight cache. /// @@ -80,7 +80,7 @@ impl BlockWeightsCache { config: BlockWeightsCacheConfig, database: D, chain: Chain, - ) -> Result { + ) -> Result { tracing::info!("Initializing weight cache this may take a while."); let long_term_weights = get_long_term_weight_in_range( @@ -121,7 +121,7 @@ impl BlockWeightsCache { &mut self, numb_blocks: usize, database: D, - ) -> Result<(), ExtendedConsensusError> { + ) -> Result<(), ContextCacheError> { if self.long_term_weights.window_len() <= numb_blocks { // More blocks to pop than we have in the cache, so just restart a new cache. *self = Self::init_from_chain_height( @@ -258,7 +258,7 @@ fn calculate_effective_median_block_weight( } /// Calculates a blocks long term weight. -pub(crate) fn calculate_block_long_term_weight( +pub fn calculate_block_long_term_weight( hf: HardFork, block_weight: usize, long_term_median: usize, @@ -287,7 +287,7 @@ async fn get_blocks_weight_in_range( range: Range, database: D, chain: Chain, -) -> Result, ExtendedConsensusError> { +) -> Result, ContextCacheError> { tracing::info!("getting block weights."); let BlockchainResponse::BlockExtendedHeaderInRange(ext_headers) = database @@ -311,7 +311,7 @@ async fn get_long_term_weight_in_range( range: Range, database: D, chain: Chain, -) -> Result, ExtendedConsensusError> { +) -> Result, ContextCacheError> { tracing::info!("getting block long term weights."); let BlockchainResponse::BlockExtendedHeaderInRange(ext_headers) = database diff --git a/consensus/fast-sync/Cargo.toml b/consensus/fast-sync/Cargo.toml index 1d7d97b45..aa9c8d2fb 100644 --- a/consensus/fast-sync/Cargo.toml +++ b/consensus/fast-sync/Cargo.toml @@ -9,11 +9,12 @@ name = "cuprate-fast-sync-create-hashes" path = "src/create.rs" [dependencies] -cuprate-blockchain = { path = "../../storage/blockchain" } -cuprate-consensus = { path = ".." } -cuprate-consensus-rules = { path = "../rules" } -cuprate-types = { path = "../../types" } -cuprate-helper = { path = "../../helper", features = ["cast"] } +cuprate-blockchain = { path = "../../storage/blockchain" } +cuprate-consensus = { path = ".." } +cuprate-consensus-rules = { path = "../rules" } +cuprate-consensus-context = { path = "../context" } +cuprate-types = { path = "../../types" } +cuprate-helper = { path = "../../helper", features = ["cast"] } clap = { workspace = true, features = ["derive", "std"] } hex = { workspace = true } @@ -27,4 +28,4 @@ tower = { workspace = true } [dev-dependencies] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/consensus/fast-sync/src/fast_sync.rs b/consensus/fast-sync/src/fast_sync.rs index ec4ea297c..3764e2170 100644 --- a/consensus/fast-sync/src/fast_sync.rs +++ b/consensus/fast-sync/src/fast_sync.rs @@ -12,10 +12,8 @@ use monero_serai::{ }; use tower::{Service, ServiceExt}; -use cuprate_consensus::{ - context::{BlockChainContextRequest, BlockChainContextResponse}, - transactions::new_tx_verification_data, -}; +use cuprate_consensus::transactions::new_tx_verification_data; +use cuprate_consensus_context::{BlockChainContextRequest, BlockChainContextResponse}; use cuprate_consensus_rules::{miner_tx::MinerTxError, ConsensusError}; use cuprate_helper::cast::u64_to_usize; use cuprate_types::{VerifiedBlockInformation, VerifiedTransactionInformation}; diff --git a/consensus/src/block.rs b/consensus/src/block.rs index ceb2cbab8..3f5d749e5 100644 --- a/consensus/src/block.rs +++ b/consensus/src/block.rs @@ -14,6 +14,9 @@ use monero_serai::{ }; use tower::{Service, ServiceExt}; +use cuprate_consensus_context::{ + BlockChainContextRequest, BlockChainContextResponse, RawBlockChainContext, +}; use cuprate_helper::asynch::rayon_spawn_async; use cuprate_types::{ AltBlockInformation, TransactionVerificationData, VerifiedBlockInformation, @@ -30,7 +33,6 @@ use cuprate_consensus_rules::{ }; use crate::{ - context::{BlockChainContextRequest, BlockChainContextResponse, RawBlockChainContext}, transactions::{VerifyTxRequest, VerifyTxResponse}, Database, ExtendedConsensusError, }; diff --git a/consensus/src/block/alt_block.rs b/consensus/src/block/alt_block.rs index 3a5ea7cbe..18c27345f 100644 --- a/consensus/src/block/alt_block.rs +++ b/consensus/src/block/alt_block.rs @@ -7,6 +7,12 @@ use std::{collections::HashMap, sync::Arc}; use monero_serai::{block::Block, transaction::Input}; use tower::{Service, ServiceExt}; +use cuprate_consensus_context::{ + difficulty::DifficultyCache, + rx_vms::RandomXVm, + weight::{self, BlockWeightsCache}, + AltChainContextCache, AltChainRequestToken, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW, +}; use cuprate_consensus_rules::{ blocks::{ check_block_pow, check_block_weight, check_timestamp, randomx_seed_height, BlockError, @@ -22,12 +28,6 @@ use cuprate_types::{ use crate::{ block::{free::pull_ordered_transactions, PreparedBlock}, - context::{ - difficulty::DifficultyCache, - rx_vms::RandomXVm, - weight::{self, BlockWeightsCache}, - AltChainContextCache, AltChainRequestToken, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW, - }, BlockChainContextRequest, BlockChainContextResponse, ExtendedConsensusError, VerifyBlockResponse, }; diff --git a/consensus/src/block/batch_prepare.rs b/consensus/src/block/batch_prepare.rs index 029a5ae6c..ef384f5da 100644 --- a/consensus/src/block/batch_prepare.rs +++ b/consensus/src/block/batch_prepare.rs @@ -5,6 +5,7 @@ use rayon::prelude::*; use tower::{Service, ServiceExt}; use tracing::instrument; +use cuprate_consensus_context::rx_vms::RandomXVm; use cuprate_consensus_rules::{ blocks::{check_block_pow, is_randomx_seed_height, randomx_seed_height, BlockError}, hard_forks::HardForkError, @@ -15,7 +16,6 @@ use cuprate_helper::asynch::rayon_spawn_async; use crate::{ block::{free::pull_ordered_transactions, PreparedBlock, PreparedBlockExPow}, - context::rx_vms::RandomXVm, transactions::new_tx_verification_data, BlockChainContextRequest, BlockChainContextResponse, ExtendedConsensusError, VerifyBlockResponse, diff --git a/consensus/src/lib.rs b/consensus/src/lib.rs index 7280f2ff5..f21d00b2e 100644 --- a/consensus/src/lib.rs +++ b/consensus/src/lib.rs @@ -24,13 +24,12 @@ use cuprate_consensus_rules::ConsensusError; mod batch_verifier; pub mod block; -pub mod context; #[cfg(test)] mod tests; pub mod transactions; pub use block::{BlockVerifierService, VerifyBlockRequest, VerifyBlockResponse}; -pub use context::{ +pub use cuprate_consensus_context::{ initialize_blockchain_context, BlockChainContext, BlockChainContextRequest, BlockChainContextResponse, BlockChainContextService, ContextConfig, }; diff --git a/consensus/src/tests/context.rs b/consensus/src/tests/context.rs index fdef0ac8d..b9c521777 100644 --- a/consensus/src/tests/context.rs +++ b/consensus/src/tests/context.rs @@ -2,15 +2,13 @@ use proptest::strategy::ValueTree; use proptest::{strategy::Strategy, test_runner::TestRunner}; use tower::ServiceExt; -use crate::{ - context::{ - initialize_blockchain_context, BlockChainContextRequest, BlockChainContextResponse, - ContextConfig, NewBlockData, - }, - tests::mock_db::*, - HardFork, +use cuprate_consensus_context::{ + initialize_blockchain_context, BlockChainContextRequest, BlockChainContextResponse, + ContextConfig, NewBlockData, }; +use crate::{tests::mock_db::*, HardFork}; + pub(crate) mod data; mod difficulty; mod hardforks; diff --git a/consensus/src/tests/context/difficulty.rs b/consensus/src/tests/context/difficulty.rs index d5027f508..f1c0fd97c 100644 --- a/consensus/src/tests/context/difficulty.rs +++ b/consensus/src/tests/context/difficulty.rs @@ -4,10 +4,10 @@ use proptest::collection::{size_range, vec}; use proptest::{prelude::*, prop_assert_eq, prop_compose, proptest}; use crate::{ - context::difficulty::*, tests::{context::data::DIF_3000000_3002000, mock_db::*}, HardFork, }; +use cuprate_consensus_context::difficulty::*; use cuprate_helper::num::median; use cuprate_types::Chain; diff --git a/consensus/src/tests/context/hardforks.rs b/consensus/src/tests/context/hardforks.rs index 17bd47f9b..f08002326 100644 --- a/consensus/src/tests/context/hardforks.rs +++ b/consensus/src/tests/context/hardforks.rs @@ -1,13 +1,11 @@ use proptest::{collection::vec, prelude::*}; +use cuprate_consensus_context::{hardforks::HardForkState, HardForkConfig}; use cuprate_consensus_rules::hard_forks::{HFInfo, HFsInfo, HardFork, NUMB_OF_HARD_FORKS}; -use crate::{ - context::{hardforks::HardForkState, HardForkConfig}, - tests::{ - context::data::{HFS_2678808_2688888, HFS_2688888_2689608}, - mock_db::*, - }, +use crate::tests::{ + context::data::{HFS_2678808_2688888, HFS_2688888_2689608}, + mock_db::*, }; const TEST_WINDOW_SIZE: usize = 25; diff --git a/consensus/src/tests/context/rx_vms.rs b/consensus/src/tests/context/rx_vms.rs index b1eba8e23..41c62796c 100644 --- a/consensus/src/tests/context/rx_vms.rs +++ b/consensus/src/tests/context/rx_vms.rs @@ -3,15 +3,13 @@ use std::collections::VecDeque; use proptest::prelude::*; use tokio::runtime::Builder; +use cuprate_consensus_context::rx_vms::{get_last_rx_seed_heights, RandomXVmCache}; use cuprate_consensus_rules::{ blocks::{is_randomx_seed_height, randomx_seed_height}, HardFork, }; -use crate::{ - context::rx_vms::{get_last_rx_seed_heights, RandomXVmCache}, - tests::mock_db::*, -}; +use crate::tests::mock_db::*; #[test] fn rx_heights_consistent() { diff --git a/consensus/src/tests/context/weight.rs b/consensus/src/tests/context/weight.rs index b23f8f80d..dab3979ec 100644 --- a/consensus/src/tests/context/weight.rs +++ b/consensus/src/tests/context/weight.rs @@ -1,11 +1,11 @@ use crate::{ - context::{ - weight::{calculate_block_long_term_weight, BlockWeightsCache}, - BlockWeightsCacheConfig, - }, tests::{context::data::BW_2850000_3050000, mock_db::*}, HardFork, }; +use cuprate_consensus_context::{ + weight::{calculate_block_long_term_weight, BlockWeightsCache}, + BlockWeightsCacheConfig, +}; use cuprate_types::Chain; pub(crate) const TEST_WEIGHT_CONFIG: BlockWeightsCacheConfig = From 4b350e897d76abfc4fdccd152ff02b966d549031 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Tue, 22 Oct 2024 12:35:54 -0400 Subject: [PATCH 4/6] consensus-context: enable workspace lints (#321) enable lints, fix 1.82 clippy --- consensus/context/Cargo.toml | 7 +++++-- consensus/context/src/difficulty.rs | 2 +- consensus/context/src/lib.rs | 6 +++++- consensus/rules/Cargo.toml | 2 +- consensus/rules/src/lib.rs | 4 ++-- consensus/rules/src/miner_tx.rs | 2 +- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/consensus/context/Cargo.toml b/consensus/context/Cargo.toml index 00804204d..f7642e8ae 100644 --- a/consensus/context/Cargo.toml +++ b/consensus/context/Cargo.toml @@ -7,8 +7,8 @@ authors = ["SyntheticBird","Boog900"] [dependencies] cuprate-consensus-rules = { path = "../rules", features = ["proptest"]} -cuprate-helper = { path = "../../helper", default-features = false, features = ["std", "cast"] } -cuprate-types = { path = "../../types", default-features = false } +cuprate-helper = { path = "../../helper", default-features = false, features = ["std", "cast", "num", "asynch"] } +cuprate-types = { path = "../../types", default-features = false, features = ["blockchain"] } futures = { workspace = true, features = ["std", "async-await"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros"]} @@ -22,3 +22,6 @@ randomx-rs = { workspace = true } rayon = { workspace = true } thread_local = { workspace = true } hex = { workspace = true } + +[lints] +workspace = true \ No newline at end of file diff --git a/consensus/context/src/difficulty.rs b/consensus/context/src/difficulty.rs index e3f558a01..1b61eb9ea 100644 --- a/consensus/context/src/difficulty.rs +++ b/consensus/context/src/difficulty.rs @@ -329,7 +329,7 @@ fn next_difficulty( } // TODO: do checked operations here and unwrap so we don't silently overflow? - (windowed_work * hf.block_time().as_secs() as u128 + time_span - 1) / time_span + (windowed_work * u128::from(hf.block_time().as_secs()) + time_span - 1) / time_span } /// Get the start and end of the window to calculate difficulty. diff --git a/consensus/context/src/lib.rs b/consensus/context/src/lib.rs index 82e601d7b..198d5a1d4 100644 --- a/consensus/context/src/lib.rs +++ b/consensus/context/src/lib.rs @@ -3,7 +3,11 @@ //! This crate contains a service to get cached context from the blockchain: [`BlockChainContext`]. //! This is used during contextual validation, this does not have all the data for contextual validation //! (outputs) for that you will need a [`Database`]. -//! + +// Used in documentation references for [`BlockChainContextRequest`] +// FIXME: should we pull in a dependency just to link docs? +use monero_serai as _; + use std::{ cmp::min, collections::HashMap, diff --git a/consensus/rules/Cargo.toml b/consensus/rules/Cargo.toml index 50117acfe..fac22bcc3 100644 --- a/consensus/rules/Cargo.toml +++ b/consensus/rules/Cargo.toml @@ -11,7 +11,7 @@ proptest = ["cuprate-types/proptest"] rayon = ["dep:rayon"] [dependencies] -cuprate-constants = { path = "../../constants", default-features = false } +cuprate-constants = { path = "../../constants", default-features = false, features = ["block"] } 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/lib.rs b/consensus/rules/src/lib.rs index 876e2f7f9..eef20c1e5 100644 --- a/consensus/rules/src/lib.rs +++ b/consensus/rules/src/lib.rs @@ -63,9 +63,9 @@ where /// An internal function that returns an iterator or a parallel iterator if the /// `rayon` feature is enabled. #[cfg(not(feature = "rayon"))] -fn try_par_iter(t: T) -> impl std::iter::Iterator +fn try_par_iter(t: T) -> impl Iterator where - T: std::iter::IntoIterator, + T: IntoIterator, { t.into_iter() } diff --git a/consensus/rules/src/miner_tx.rs b/consensus/rules/src/miner_tx.rs index 5221ee55a..bb3b004ad 100644 --- a/consensus/rules/src/miner_tx.rs +++ b/consensus/rules/src/miner_tx.rs @@ -68,7 +68,7 @@ pub fn calculate_block_reward( .unwrap(); let effective_median_bw: u128 = median_bw.try_into().unwrap(); - (((base_reward as u128 * multiplicand) / effective_median_bw) / effective_median_bw) + (((u128::from(base_reward) * multiplicand) / effective_median_bw) / effective_median_bw) .try_into() .unwrap() } From b8e2d00af492a5262185d2dda6a42f6bd3b6a7ea Mon Sep 17 00:00:00 2001 From: SyntheticBird <118022351+SyntheticBird45@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:10:33 +0200 Subject: [PATCH 5/6] storage: Add common amounts commitment lookup table (#323) Add common ammounts commitment lookup table - Implements `compute_zero_commitment` function in `cuprate-helper::crypto` module. - Added test that compare the function output with the correct calculation. - Use of a constant-time algorithm for the lookup table. - Added according documentation --- constants/Cargo.toml | 2 +- helper/Cargo.toml | 14 +-- helper/src/crypto.rs | 122 +++++++++++++++++++++++++++ helper/src/lib.rs | 3 + storage/blockchain/Cargo.toml | 2 +- storage/blockchain/src/ops/output.rs | 9 +- storage/blockchain/src/ops/tx.rs | 9 +- 7 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 helper/src/crypto.rs diff --git a/constants/Cargo.toml b/constants/Cargo.toml index 6d3e031bf..5ce37325e 100644 --- a/constants/Cargo.toml +++ b/constants/Cargo.toml @@ -19,4 +19,4 @@ rpc = [] [dev-dependencies] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/helper/Cargo.toml b/helper/Cargo.toml index 111c6f024..c70efb0ce 100644 --- a/helper/Cargo.toml +++ b/helper/Cargo.toml @@ -16,6 +16,7 @@ atomic = ["dep:crossbeam"] asynch = ["dep:futures", "dep:rayon"] cast = [] constants = [] +crypto = ["dep:curve25519-dalek", "dep:monero-serai", "std"] fs = ["dep:dirs"] num = [] map = ["cast", "dep:monero-serai", "dep:cuprate-constants"] @@ -26,12 +27,13 @@ tx = ["dep:monero-serai"] [dependencies] cuprate-constants = { path = "../constants", optional = true, features = ["block"] } -crossbeam = { workspace = true, optional = true } -chrono = { workspace = true, optional = true, features = ["std", "clock"] } -dirs = { workspace = true, optional = true } -futures = { workspace = true, optional = true, features = ["std"] } -monero-serai = { workspace = true, optional = true } -rayon = { workspace = true, optional = true } +chrono = { workspace = true, optional = true, features = ["std", "clock"] } +crossbeam = { workspace = true, optional = true } +curve25519-dalek = { workspace = true, optional = true } +dirs = { workspace = true, optional = true } +futures = { workspace = true, optional = true, features = ["std"] } +monero-serai = { workspace = true, optional = true } +rayon = { workspace = true, optional = true } # This is kinda a stupid work around. # [thread] needs to activate one of these libs (windows|libc) diff --git a/helper/src/crypto.rs b/helper/src/crypto.rs new file mode 100644 index 000000000..1a27cd301 --- /dev/null +++ b/helper/src/crypto.rs @@ -0,0 +1,122 @@ +//! Crypto related functions and runtime initialized constants + +//---------------------------------------------------------------------------------------------------- Use +use std::sync::LazyLock; + +use curve25519_dalek::{ + constants::ED25519_BASEPOINT_POINT, edwards::VartimeEdwardsPrecomputation, + traits::VartimePrecomputedMultiscalarMul, EdwardsPoint, Scalar, +}; +use monero_serai::generators::H; + +//---------------------------------------------------------------------------------------------------- Pre-computation + +/// This is the decomposed amount table containing the mandatory Pre-RCT amounts. It is used to pre-compute +/// zero commitments at runtime. +/// +/// Defined at: +/// - +#[rustfmt::skip] +pub const ZERO_COMMITMENT_DECOMPOSED_AMOUNT: [u64; 172] = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 20, 30, 40, 50, 60, 70, 80, 90, + 100, 200, 300, 400, 500, 600, 700, 800, 900, + 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, + 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, + 100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000, + 1000000, 2000000, 3000000, 4000000, 5000000, 6000000, 7000000, 8000000, 9000000, + 10000000, 20000000, 30000000, 40000000, 50000000, 60000000, 70000000, 80000000, 90000000, + 100000000, 200000000, 300000000, 400000000, 500000000, 600000000, 700000000, 800000000, 900000000, + 1000000000, 2000000000, 3000000000, 4000000000, 5000000000, 6000000000, 7000000000, 8000000000, 9000000000, + 10000000000, 20000000000, 30000000000, 40000000000, 50000000000, 60000000000, 70000000000, 80000000000, 90000000000, + 100000000000, 200000000000, 300000000000, 400000000000, 500000000000, 600000000000, 700000000000, 800000000000, 900000000000, + 1000000000000, 2000000000000, 3000000000000, 4000000000000, 5000000000000, 6000000000000, 7000000000000, 8000000000000, 9000000000000, + 10000000000000, 20000000000000, 30000000000000, 40000000000000, 50000000000000, 60000000000000, 70000000000000, 80000000000000, 90000000000000, + 100000000000000, 200000000000000, 300000000000000, 400000000000000, 500000000000000, 600000000000000, 700000000000000, 800000000000000, 900000000000000, + 1000000000000000, 2000000000000000, 3000000000000000, 4000000000000000, 5000000000000000, 6000000000000000, 7000000000000000, 8000000000000000, 9000000000000000, + 10000000000000000, 20000000000000000, 30000000000000000, 40000000000000000, 50000000000000000, 60000000000000000, 70000000000000000, 80000000000000000, 90000000000000000, + 100000000000000000, 200000000000000000, 300000000000000000, 400000000000000000, 500000000000000000, 600000000000000000, 700000000000000000, 800000000000000000, 900000000000000000, + 1000000000000000000, 2000000000000000000, 3000000000000000000, 4000000000000000000, 5000000000000000000, 6000000000000000000, 7000000000000000000, 8000000000000000000, 9000000000000000000, + 10000000000000000000 +]; + +/// Runtime initialized [`H`] generator. +static H_PRECOMP: LazyLock = + LazyLock::new(|| VartimeEdwardsPrecomputation::new([*H, ED25519_BASEPOINT_POINT])); + +/// Runtime initialized zero commitment lookup table +/// +/// # Invariant +/// This function assumes that the [`ZERO_COMMITMENT_DECOMPOSED_AMOUNT`] +/// table is sorted. +pub static ZERO_COMMITMENT_LOOKUP_TABLE: LazyLock<[EdwardsPoint; 172]> = LazyLock::new(|| { + let mut lookup_table: [EdwardsPoint; 172] = [ED25519_BASEPOINT_POINT; 172]; + + for (i, amount) in ZERO_COMMITMENT_DECOMPOSED_AMOUNT.into_iter().enumerate() { + lookup_table[i] = ED25519_BASEPOINT_POINT + *H * Scalar::from(amount); + } + + lookup_table +}); + +//---------------------------------------------------------------------------------------------------- Free functions + +/// This function computes the zero commitment given a specific amount. +/// +/// It will first attempt to lookup into the table of known Pre-RCT value. +/// Compute it otherwise. +#[expect(clippy::cast_possible_truncation)] +pub fn compute_zero_commitment(amount: u64) -> EdwardsPoint { + // OPTIMIZATION: Unlike monerod which execute a linear search across its lookup + // table (O(n)). Cuprate is making use of an arithmetic based constant time + // version (O(1)). It has been benchmarked in both hit and miss scenarios against + // a binary search lookup (O(log2(n))). To understand the following algorithm it + // is important to observe the pattern that follows the values of + // [`ZERO_COMMITMENT_DECOMPOSED_AMOUNT`]. + + // First obtain the logarithm base 10 of the amount. and extend it back to obtain + // the amount without its most significant digit. + let Some(log) = amount.checked_ilog10() else { + // amount = 0 so H component is 0. + return ED25519_BASEPOINT_POINT; + }; + let div = 10_u64.pow(log); + + // Extract the most significant digit. + let most_significant_digit = amount / div; + + // If the *rounded* version is different than the exact amount. Then + // there aren't only trailing zeroes behind the most significant digit. + // The amount is not part of the table and can calculated apart. + if most_significant_digit * div != amount { + return H_PRECOMP.vartime_multiscalar_mul([Scalar::from(amount), Scalar::ONE]); + } + + // Calculating the index back by progressing within the powers of 10. + // The index of the first value in the cached amount's row. + let row_start = u64::from(log) * 9; + // The index of the cached amount + let index = (most_significant_digit - 1 + row_start) as usize; + + ZERO_COMMITMENT_LOOKUP_TABLE[index] +} + +//---------------------------------------------------------------------------------------------------- Tests +#[cfg(test)] +mod test { + use curve25519_dalek::{traits::VartimePrecomputedMultiscalarMul, Scalar}; + + use crate::crypto::{compute_zero_commitment, H_PRECOMP, ZERO_COMMITMENT_DECOMPOSED_AMOUNT}; + + #[test] + /// Compare the output of `compute_zero_commitment` for all + /// preRCT decomposed amounts against their actual computation. + /// + /// Assert that the lookup table returns the correct commitments + fn compare_lookup_with_computation() { + for amount in ZERO_COMMITMENT_DECOMPOSED_AMOUNT { + let commitment = H_PRECOMP.vartime_multiscalar_mul([Scalar::from(amount), Scalar::ONE]); + assert!(commitment == compute_zero_commitment(amount)); + } + } +} diff --git a/helper/src/lib.rs b/helper/src/lib.rs index bfd2fd607..47d47a23a 100644 --- a/helper/src/lib.rs +++ b/helper/src/lib.rs @@ -30,6 +30,9 @@ pub mod time; #[cfg(feature = "tx")] pub mod tx; + +#[cfg(feature = "crypto")] +pub mod crypto; //---------------------------------------------------------------------------------------------------- Private Usage //---------------------------------------------------------------------------------------------------- diff --git a/storage/blockchain/Cargo.toml b/storage/blockchain/Cargo.toml index 005791107..414b78432 100644 --- a/storage/blockchain/Cargo.toml +++ b/storage/blockchain/Cargo.toml @@ -20,7 +20,7 @@ service = ["dep:thread_local", "dep:rayon", "cuprate-helper/thread"] [dependencies] cuprate-database = { path = "../database" } cuprate-database-service = { path = "../service" } -cuprate-helper = { path = "../../helper", features = ["fs", "map"] } +cuprate-helper = { path = "../../helper", features = ["fs", "map", "crypto"] } cuprate-types = { path = "../../types", features = ["blockchain"] } cuprate-pruning = { path = "../../pruning" } diff --git a/storage/blockchain/src/ops/output.rs b/storage/blockchain/src/ops/output.rs index 1c7c1d789..14c209ab6 100644 --- a/storage/blockchain/src/ops/output.rs +++ b/storage/blockchain/src/ops/output.rs @@ -1,12 +1,13 @@ //! Output functions. //---------------------------------------------------------------------------------------------------- Import -use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::CompressedEdwardsY, Scalar}; -use monero_serai::{generators::H, transaction::Timelock}; +use curve25519_dalek::edwards::CompressedEdwardsY; +use monero_serai::transaction::Timelock; use cuprate_database::{ RuntimeError, {DatabaseRo, DatabaseRw}, }; +use cuprate_helper::crypto::compute_zero_commitment; use cuprate_helper::map::u64_to_timelock; use cuprate_types::OutputOnChain; @@ -155,9 +156,7 @@ pub fn output_to_output_on_chain( amount: Amount, table_tx_unlock_time: &impl DatabaseRo, ) -> Result { - // FIXME: implement lookup table for common values: - // - let commitment = ED25519_BASEPOINT_POINT + *H * Scalar::from(amount); + let commitment = compute_zero_commitment(amount); let time_lock = if output .output_flags diff --git a/storage/blockchain/src/ops/tx.rs b/storage/blockchain/src/ops/tx.rs index c9799a2ce..5a60ad53f 100644 --- a/storage/blockchain/src/ops/tx.rs +++ b/storage/blockchain/src/ops/tx.rs @@ -2,10 +2,10 @@ //---------------------------------------------------------------------------------------------------- Import use bytemuck::TransparentWrapper; -use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, Scalar}; use monero_serai::transaction::{Input, Timelock, Transaction}; use cuprate_database::{DatabaseRo, DatabaseRw, RuntimeError, StorableVec}; +use cuprate_helper::crypto::compute_zero_commitment; use crate::{ ops::{ @@ -136,12 +136,9 @@ pub fn add_tx( .enumerate() .map(|(i, output)| { // Create commitment. - // - // FIXME: implement lookup table for common values: - // + let commitment = if miner_tx { - ED25519_BASEPOINT_POINT - + *monero_serai::generators::H * Scalar::from(output.amount.unwrap_or(0)) + compute_zero_commitment(output.amount.unwrap_or(0)) } else { proofs .as_ref() From 63216aecaead915550be93236faf6aeeebf0e04f Mon Sep 17 00:00:00 2001 From: SyntheticBird <118022351+SyntheticBird45@users.noreply.github.com> Date: Fri, 25 Oct 2024 00:12:30 +0200 Subject: [PATCH 6/6] workspace: Defines cuprate members as workspace dependencies (#326) Defines cuprate members as workspace dependencies - Defines cuprate members as workspace dependencies - Changed all `path` import into `workspace = true` Co-authored-by: Boog900 --- Cargo.toml | 29 ++++++++++++++++++++ binaries/cuprated/Cargo.toml | 50 +++++++++++++++++----------------- consensus/Cargo.toml | 12 ++++---- consensus/context/Cargo.toml | 8 +++--- consensus/fast-sync/Cargo.toml | 12 ++++---- consensus/rules/Cargo.toml | 8 +++--- helper/Cargo.toml | 2 +- net/epee-encoding/Cargo.toml | 6 ++-- net/levin/Cargo.toml | 4 +-- net/wire/Cargo.toml | 10 +++---- p2p/address-book/Cargo.toml | 10 +++---- p2p/p2p-core/Cargo.toml | 10 +++---- p2p/p2p/Cargo.toml | 22 +++++++-------- pruning/Cargo.toml | 4 +-- rpc/interface/Cargo.toml | 10 +++---- rpc/types/Cargo.toml | 10 +++---- storage/blockchain/Cargo.toml | 16 +++++------ storage/service/Cargo.toml | 4 +-- storage/txpool/Cargo.toml | 12 ++++---- test-utils/Cargo.toml | 10 +++---- types/Cargo.toml | 8 +++--- 21 files changed, 143 insertions(+), 114 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2ef99d62d..386586328 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,35 @@ opt-level = 1 opt-level = 3 [workspace.dependencies] +# Cuprate members +cuprate-fast-sync = { path = "consensus/fast-sync" ,default-features = false} +cuprate-consensus-rules = { path = "consensus/rules" ,default-features = false} +cuprate-constants = { path = "constants" ,default-features = false} +cuprate-consensus = { path = "consensus" ,default-features = false} +cuprate-consensus-context = { path = "consensus/context" ,default-features = false} +cuprate-cryptonight = { path = "cryptonight" ,default-features = false} +cuprate-helper = { path = "helper" ,default-features = false} +cuprate-epee-encoding = { path = "net/epee-encoding" ,default-features = false} +cuprate-fixed-bytes = { path = "net/fixed-bytes" ,default-features = false} +cuprate-levin = { path = "net/levin" ,default-features = false} +cuprate-wire = { path = "net/wire" ,default-features = false} +cuprate-p2p = { path = "p2p/p2p" ,default-features = false} +cuprate-p2p-core = { path = "p2p/p2p-core" ,default-features = false} +cuprate-dandelion-tower = { path = "p2p/dandelion-tower" ,default-features = false} +cuprate-async-buffer = { path = "p2p/async-buffer" ,default-features = false} +cuprate-address-book = { path = "p2p/address-book" ,default-features = false} +cuprate-blockchain = { path = "storage/blockchain" ,default-features = false} +cuprate-database = { path = "storage/database" ,default-features = false} +cuprate-database-service = { path = "storage/service" ,default-features = false} +cuprate-txpool = { path = "storage/txpool" ,default-features = false} +cuprate-pruning = { path = "pruning" ,default-features = false} +cuprate-test-utils = { path = "test-utils" ,default-features = false} +cuprate-types = { path = "types" ,default-features = false} +cuprate-json-rpc = { path = "rpc/json-rpc" ,default-features = false} +cuprate-rpc-types = { path = "rpc/types" ,default-features = false} +cuprate-rpc-interface = { path = "rpc/interface" ,default-features = false} + +# External dependencies anyhow = { version = "1.0.89", default-features = false } async-trait = { version = "0.1.82", default-features = false } bitflags = { version = "2.6.0", default-features = false } diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index 59fa9784e..2f22be0c9 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -9,31 +9,31 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/binaries/cuprated" [dependencies] # TODO: after v1.0.0, remove unneeded dependencies. -cuprate-consensus = { path = "../../consensus" } -cuprate-fast-sync = { path = "../../consensus/fast-sync" } -cuprate-consensus-context = { path = "../../consensus/context" } -cuprate-consensus-rules = { path = "../../consensus/rules" } -cuprate-cryptonight = { path = "../../cryptonight" } -cuprate-helper = { path = "../../helper" } -cuprate-epee-encoding = { path = "../../net/epee-encoding" } -cuprate-fixed-bytes = { path = "../../net/fixed-bytes" } -cuprate-levin = { path = "../../net/levin" } -cuprate-wire = { path = "../../net/wire" } -cuprate-p2p = { path = "../../p2p/p2p" } -cuprate-p2p-core = { path = "../../p2p/p2p-core" } -cuprate-dandelion-tower = { path = "../../p2p/dandelion-tower" } -cuprate-async-buffer = { path = "../../p2p/async-buffer" } -cuprate-address-book = { path = "../../p2p/address-book" } -cuprate-blockchain = { path = "../../storage/blockchain", features = ["service"] } -cuprate-database-service = { path = "../../storage/service" } -cuprate-txpool = { path = "../../storage/txpool" } -cuprate-database = { path = "../../storage/database" } -cuprate-pruning = { path = "../../pruning" } -cuprate-test-utils = { path = "../../test-utils" } -cuprate-types = { path = "../../types" } -cuprate-json-rpc = { path = "../../rpc/json-rpc" } -cuprate-rpc-interface = { path = "../../rpc/interface" } -cuprate-rpc-types = { path = "../../rpc/types" } +cuprate-consensus = { workspace = true } +cuprate-fast-sync = { workspace = true } +cuprate-consensus-context = { workspace = true } +cuprate-consensus-rules = { workspace = true } +cuprate-cryptonight = { workspace = true } +cuprate-helper = { workspace = true } +cuprate-epee-encoding = { workspace = true } +cuprate-fixed-bytes = { workspace = true } +cuprate-levin = { workspace = true } +cuprate-wire = { workspace = true } +cuprate-p2p = { workspace = true } +cuprate-p2p-core = { workspace = true } +cuprate-dandelion-tower = { workspace = true } +cuprate-async-buffer = { workspace = true } +cuprate-address-book = { workspace = true } +cuprate-blockchain = { workspace = true, features = ["service"] } +cuprate-database-service = { workspace = true } +cuprate-txpool = { workspace = true } +cuprate-database = { workspace = true } +cuprate-pruning = { workspace = true } +cuprate-test-utils = { workspace = true } +cuprate-types = { workspace = true } +cuprate-json-rpc = { workspace = true } +cuprate-rpc-interface = { workspace = true } +cuprate-rpc-types = { workspace = true } # TODO: after v1.0.0, remove unneeded dependencies. anyhow = { workspace = true } diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index 1fdee89a7..8b732a079 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -8,10 +8,10 @@ authors = ["Boog900"] repository = "https://github.com/Cuprate/cuprate/tree/main/consensus" [dependencies] -cuprate-helper = { path = "../helper", default-features = false, features = ["std", "asynch", "num"] } -cuprate-consensus-rules = { path = "./rules", features = ["rayon"] } -cuprate-types = { path = "../types" } -cuprate-consensus-context = { path = "./context" } +cuprate-helper = { workspace = true, default-features = false, features = ["std", "asynch", "num"] } +cuprate-consensus-rules = { workspace = true, features = ["rayon"] } +cuprate-types = { workspace = true } +cuprate-consensus-context = { workspace = true } cfg-if = { workspace = true } thiserror = { workspace = true } @@ -28,8 +28,8 @@ hex = { workspace = true } rand = { workspace = true } [dev-dependencies] -cuprate-test-utils = { path = "../test-utils" } -cuprate-consensus-rules = {path = "./rules", features = ["proptest"]} +cuprate-test-utils = { workspace = true } +cuprate-consensus-rules = { workspace = true, features = ["proptest"]} hex-literal = { workspace = true } curve25519-dalek = { workspace = true } diff --git a/consensus/context/Cargo.toml b/consensus/context/Cargo.toml index f7642e8ae..767904649 100644 --- a/consensus/context/Cargo.toml +++ b/consensus/context/Cargo.toml @@ -6,9 +6,9 @@ license = "MIT" authors = ["SyntheticBird","Boog900"] [dependencies] -cuprate-consensus-rules = { path = "../rules", features = ["proptest"]} -cuprate-helper = { path = "../../helper", default-features = false, features = ["std", "cast", "num", "asynch"] } -cuprate-types = { path = "../../types", default-features = false, features = ["blockchain"] } +cuprate-consensus-rules = { workspace = true, features = ["proptest"]} +cuprate-helper = { workspace = true, default-features = false, features = ["std", "cast", "num", "asynch"] } +cuprate-types = { workspace = true, default-features = false, features = ["blockchain"] } futures = { workspace = true, features = ["std", "async-await"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros"]} @@ -24,4 +24,4 @@ thread_local = { workspace = true } hex = { workspace = true } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/consensus/fast-sync/Cargo.toml b/consensus/fast-sync/Cargo.toml index aa9c8d2fb..8e732a6fc 100644 --- a/consensus/fast-sync/Cargo.toml +++ b/consensus/fast-sync/Cargo.toml @@ -9,12 +9,12 @@ name = "cuprate-fast-sync-create-hashes" path = "src/create.rs" [dependencies] -cuprate-blockchain = { path = "../../storage/blockchain" } -cuprate-consensus = { path = ".." } -cuprate-consensus-rules = { path = "../rules" } -cuprate-consensus-context = { path = "../context" } -cuprate-types = { path = "../../types" } -cuprate-helper = { path = "../../helper", features = ["cast"] } +cuprate-blockchain = { workspace = true } +cuprate-consensus = { workspace = true } +cuprate-consensus-rules = { workspace = true } +cuprate-consensus-context = { workspace = true } +cuprate-types = { workspace = true } +cuprate-helper = { workspace = true, features = ["cast"] } clap = { workspace = true, features = ["derive", "std"] } hex = { workspace = true } diff --git a/consensus/rules/Cargo.toml b/consensus/rules/Cargo.toml index fac22bcc3..8999cbcfb 100644 --- a/consensus/rules/Cargo.toml +++ b/consensus/rules/Cargo.toml @@ -11,10 +11,10 @@ proptest = ["cuprate-types/proptest"] rayon = ["dep:rayon"] [dependencies] -cuprate-constants = { path = "../../constants", default-features = false, features = ["block"] } -cuprate-helper = { path = "../../helper", default-features = false, features = ["std", "cast"] } -cuprate-types = { path = "../../types", default-features = false } -cuprate-cryptonight = {path = "../../cryptonight"} +cuprate-constants = { workspace = true, default-features = false, features = ["block"] } +cuprate-helper = { workspace = true, default-features = false, features = ["std", "cast"] } +cuprate-types = { workspace = true, default-features = false } +cuprate-cryptonight = { workspace = true } monero-serai = { workspace = true, features = ["std"] } curve25519-dalek = { workspace = true, features = ["alloc", "zeroize", "precomputed-tables"] } diff --git a/helper/Cargo.toml b/helper/Cargo.toml index c70efb0ce..ad78a4485 100644 --- a/helper/Cargo.toml +++ b/helper/Cargo.toml @@ -25,7 +25,7 @@ thread = ["std", "dep:target_os_lib"] tx = ["dep:monero-serai"] [dependencies] -cuprate-constants = { path = "../constants", optional = true, features = ["block"] } +cuprate-constants = { workspace = true, optional = true, features = ["block"] } chrono = { workspace = true, optional = true, features = ["std", "clock"] } crossbeam = { workspace = true, optional = true } diff --git a/net/epee-encoding/Cargo.toml b/net/epee-encoding/Cargo.toml index c021e4296..4724e2d0a 100644 --- a/net/epee-encoding/Cargo.toml +++ b/net/epee-encoding/Cargo.toml @@ -15,8 +15,8 @@ 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 } +cuprate-helper = { workspace = true, default-features = false, features = ["cast"] } +cuprate-fixed-bytes = { workspace = true, default-features = false } paste = "1.0.15" ref-cast = "1.0.23" @@ -27,4 +27,4 @@ thiserror = { workspace = true, optional = true} hex = { workspace = true, features = ["default"] } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/net/levin/Cargo.toml b/net/levin/Cargo.toml index 68c32e544..a9f3c1f20 100644 --- a/net/levin/Cargo.toml +++ b/net/levin/Cargo.toml @@ -12,7 +12,7 @@ default = [] tracing = ["dep:tracing", "tokio-util/tracing"] [dependencies] -cuprate-helper = { path = "../../helper", default-features = false, features = ["cast"] } +cuprate-helper = { workspace = true, default-features = false, features = ["cast"] } cfg-if = { workspace = true } thiserror = { workspace = true } @@ -30,4 +30,4 @@ tokio = { workspace = true, features = ["full"] } futures = { workspace = true, features = ["std"] } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/net/wire/Cargo.toml b/net/wire/Cargo.toml index 0b77cf1b0..b500a2884 100644 --- a/net/wire/Cargo.toml +++ b/net/wire/Cargo.toml @@ -11,11 +11,11 @@ default = [] tracing = ["cuprate-levin/tracing"] [dependencies] -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 = ["map"] } +cuprate-levin = { workspace = true } +cuprate-epee-encoding = { workspace = true } +cuprate-fixed-bytes = { workspace = true } +cuprate-types = { workspace = true, default-features = false, features = ["epee"] } +cuprate-helper = { workspace = true, default-features = false, features = ["map"] } bitflags = { workspace = true, features = ["std"] } bytes = { workspace = true, features = ["std"] } diff --git a/p2p/address-book/Cargo.toml b/p2p/address-book/Cargo.toml index 9afc25526..9cbba717a 100644 --- a/p2p/address-book/Cargo.toml +++ b/p2p/address-book/Cargo.toml @@ -7,9 +7,9 @@ authors = ["Boog900"] [dependencies] -cuprate-constants = { path = "../../constants" } -cuprate-pruning = { path = "../../pruning" } -cuprate-p2p-core = { path = "../p2p-core" } +cuprate-constants = { workspace = true } +cuprate-pruning = { workspace = true } +cuprate-p2p-core = { workspace = true } tower = { workspace = true, features = ["util"] } tokio = { workspace = true, features = ["time", "fs", "rt"]} @@ -26,9 +26,9 @@ rand = { workspace = true, features = ["std", "std_rng"] } borsh = { workspace = true, features = ["derive", "std"]} [dev-dependencies] -cuprate-test-utils = {path = "../../test-utils"} +cuprate-test-utils = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "macros"]} [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/p2p/p2p-core/Cargo.toml b/p2p/p2p-core/Cargo.toml index a30590fac..0a6aaf384 100644 --- a/p2p/p2p-core/Cargo.toml +++ b/p2p/p2p-core/Cargo.toml @@ -10,9 +10,9 @@ default = ["borsh"] borsh = ["dep:borsh", "cuprate-pruning/borsh"] [dependencies] -cuprate-helper = { path = "../../helper", features = ["asynch"], default-features = false } -cuprate-wire = { path = "../../net/wire", features = ["tracing"] } -cuprate-pruning = { path = "../../pruning" } +cuprate-helper = { workspace = true, features = ["asynch"], default-features = false } +cuprate-wire = { workspace = true, features = ["tracing"] } +cuprate-pruning = { workspace = true } tokio = { workspace = true, features = ["net", "sync", "macros", "time", "rt", "rt-multi-thread"]} tokio-util = { workspace = true, features = ["codec"] } @@ -29,10 +29,10 @@ hex-literal = { workspace = true } borsh = { workspace = true, features = ["derive", "std"], optional = true } [dev-dependencies] -cuprate-test-utils = { path = "../../test-utils" } +cuprate-test-utils = { workspace = true } hex = { workspace = true, features = ["std"] } tokio-test = { workspace = true } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/p2p/p2p/Cargo.toml b/p2p/p2p/Cargo.toml index 3444b5ef9..866fb918f 100644 --- a/p2p/p2p/Cargo.toml +++ b/p2p/p2p/Cargo.toml @@ -6,15 +6,15 @@ license = "MIT" authors = ["Boog900"] [dependencies] -cuprate-constants = { path = "../../constants" } -cuprate-fixed-bytes = { path = "../../net/fixed-bytes" } -cuprate-wire = { path = "../../net/wire" } -cuprate-p2p-core = { path = "../p2p-core", features = ["borsh"] } -cuprate-address-book = { path = "../address-book" } -cuprate-pruning = { path = "../../pruning" } -cuprate-helper = { path = "../../helper", features = ["asynch"], default-features = false } -cuprate-async-buffer = { path = "../async-buffer" } -cuprate-types = { path = "../../types", default-features = false } +cuprate-constants = { workspace = true } +cuprate-fixed-bytes = { workspace = true } +cuprate-wire = { workspace = true } +cuprate-p2p-core = { workspace = true, features = ["borsh"] } +cuprate-address-book = { workspace = true } +cuprate-pruning = { workspace = true } +cuprate-helper = { workspace = true, features = ["asynch"], default-features = false } +cuprate-async-buffer = { workspace = true } +cuprate-types = { workspace = true, default-features = false } monero-serai = { workspace = true, features = ["std"] } @@ -35,10 +35,10 @@ tracing = { workspace = true, features = ["std", "attributes"] } borsh = { workspace = true, features = ["derive", "std"] } [dev-dependencies] -cuprate-test-utils = { path = "../../test-utils" } +cuprate-test-utils = { workspace = true } indexmap = { workspace = true } proptest = { workspace = true } tokio-test = { workspace = true } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/pruning/Cargo.toml b/pruning/Cargo.toml index e898fd5ee..6fcc74e25 100644 --- a/pruning/Cargo.toml +++ b/pruning/Cargo.toml @@ -10,11 +10,11 @@ default = [] borsh = ["dep:borsh"] [dependencies] -cuprate-constants = { path = "../constants" } +cuprate-constants = { workspace = true } thiserror = { workspace = true } borsh = { workspace = true, features = ["derive", "std"], optional = true } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/rpc/interface/Cargo.toml b/rpc/interface/Cargo.toml index 00f7a228e..ef62d349a 100644 --- a/rpc/interface/Cargo.toml +++ b/rpc/interface/Cargo.toml @@ -13,10 +13,10 @@ default = ["dummy", "serde"] dummy = [] [dependencies] -cuprate-epee-encoding = { path = "../../net/epee-encoding", default-features = false } -cuprate-json-rpc = { path = "../json-rpc", default-features = false } -cuprate-rpc-types = { path = "../types", features = ["serde", "epee"], default-features = false } -cuprate-helper = { path = "../../helper", features = ["asynch"], default-features = false } +cuprate-epee-encoding = { workspace = true, default-features = false } +cuprate-json-rpc = { workspace = true, default-features = false } +cuprate-rpc-types = { workspace = true, features = ["serde", "epee"], default-features = false } +cuprate-helper = { workspace = true, features = ["asynch"], default-features = false } anyhow = { workspace = true } axum = { version = "0.7.5", features = ["json"], default-features = false } @@ -26,7 +26,7 @@ paste = { workspace = true } futures = { workspace = true } [dev-dependencies] -cuprate-test-utils = { path = "../../test-utils" } +cuprate-test-utils = { workspace = true } axum = { version = "0.7.5", features = ["json", "tokio", "http2"] } serde_json = { workspace = true, features = ["std"] } diff --git a/rpc/types/Cargo.toml b/rpc/types/Cargo.toml index cfe7e47d5..e9ca5296f 100644 --- a/rpc/types/Cargo.toml +++ b/rpc/types/Cargo.toml @@ -14,18 +14,18 @@ serde = ["dep:serde", "cuprate-fixed-bytes/serde"] epee = ["dep:cuprate-epee-encoding"] [dependencies] -cuprate-epee-encoding = { path = "../../net/epee-encoding", optional = true } -cuprate-fixed-bytes = { path = "../../net/fixed-bytes" } -cuprate-types = { path = "../../types", default-features = false, features = ["epee", "serde"] } +cuprate-epee-encoding = { workspace = true, optional = true } +cuprate-fixed-bytes = { workspace = true } +cuprate-types = { workspace = true, default-features = false, features = ["epee", "serde"] } paste = { workspace = true } serde = { workspace = true, optional = true } [dev-dependencies] -cuprate-test-utils = { path = "../../test-utils" } +cuprate-test-utils = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/storage/blockchain/Cargo.toml b/storage/blockchain/Cargo.toml index 414b78432..d0a43b3b1 100644 --- a/storage/blockchain/Cargo.toml +++ b/storage/blockchain/Cargo.toml @@ -18,11 +18,11 @@ redb-memory = ["cuprate-database/redb-memory"] service = ["dep:thread_local", "dep:rayon", "cuprate-helper/thread"] [dependencies] -cuprate-database = { path = "../database" } -cuprate-database-service = { path = "../service" } -cuprate-helper = { path = "../../helper", features = ["fs", "map", "crypto"] } -cuprate-types = { path = "../../types", features = ["blockchain"] } -cuprate-pruning = { path = "../../pruning" } +cuprate-database = { workspace = true } +cuprate-database-service = { workspace = true } +cuprate-helper = { workspace = true, features = ["fs", "map", "crypto"] } +cuprate-types = { workspace = true, features = ["blockchain"] } +cuprate-pruning = { workspace = true } bitflags = { workspace = true, features = ["std", "serde", "bytemuck"] } bytemuck = { workspace = true, features = ["must_cast", "derive", "min_const_generics", "extern_crate_alloc"] } @@ -37,9 +37,9 @@ thread_local = { workspace = true, optional = true } rayon = { workspace = true, optional = true } [dev-dependencies] -cuprate-constants = { path = "../../constants" } -cuprate-helper = { path = "../../helper", features = ["thread", "cast"] } -cuprate-test-utils = { path = "../../test-utils" } +cuprate-constants = { workspace = true } +cuprate-helper = { workspace = true, features = ["thread", "cast"] } +cuprate-test-utils = { workspace = true } tokio = { workspace = true, features = ["full"] } tempfile = { workspace = true } diff --git a/storage/service/Cargo.toml b/storage/service/Cargo.toml index ed46b355e..fa6971c56 100644 --- a/storage/service/Cargo.toml +++ b/storage/service/Cargo.toml @@ -9,8 +9,8 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/storage/service" keywords = ["cuprate", "service", "database"] [dependencies] -cuprate-database = { path = "../database" } -cuprate-helper = { path = "../../helper", features = ["fs", "thread", "map"] } +cuprate-database = { workspace = true } +cuprate-helper = { workspace = true, features = ["fs", "thread", "map"] } serde = { workspace = true, optional = true } rayon = { workspace = true } diff --git a/storage/txpool/Cargo.toml b/storage/txpool/Cargo.toml index 70211d9eb..b9d42181f 100644 --- a/storage/txpool/Cargo.toml +++ b/storage/txpool/Cargo.toml @@ -19,10 +19,10 @@ service = ["dep:tower", "dep:rayon", "dep:cuprate-database-service"] serde = ["dep:serde", "cuprate-database/serde", "cuprate-database-service/serde"] [dependencies] -cuprate-database = { path = "../database", features = ["heed"] } -cuprate-database-service = { path = "../service", optional = true } -cuprate-types = { path = "../../types" } -cuprate-helper = { path = "../../helper", default-features = false, features = ["constants"] } +cuprate-database = { workspace = true, features = ["heed"] } +cuprate-database-service = { workspace = true, optional = true } +cuprate-types = { workspace = true } +cuprate-helper = { workspace = true, default-features = false, features = ["constants"] } monero-serai = { workspace = true, features = ["std"] } bytemuck = { workspace = true, features = ["must_cast", "derive", "min_const_generics", "extern_crate_alloc"] } @@ -36,11 +36,11 @@ rayon = { workspace = true, optional = true } serde = { workspace = true, optional = true } [dev-dependencies] -cuprate-test-utils = { path = "../../test-utils" } +cuprate-test-utils = { workspace = true } tokio = { workspace = true } tempfile = { workspace = true } hex-literal = { workspace = true } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index abf7ee44a..4eb56844f 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -6,10 +6,10 @@ license = "MIT" authors = ["Boog900", "hinto-janai"] [dependencies] -cuprate-types = { path = "../types" } -cuprate-helper = { path = "../helper", features = ["map", "tx"] } -cuprate-wire = { path = "../net/wire" } -cuprate-p2p-core = { path = "../p2p/p2p-core", features = ["borsh"] } +cuprate-types = { workspace = true } +cuprate-helper = { workspace = true, features = ["map", "tx"] } +cuprate-wire = { workspace = true } +cuprate-p2p-core = { workspace = true, features = ["borsh"] } hex = { workspace = true } hex-literal = { workspace = true } @@ -31,4 +31,4 @@ hex = { workspace = true } pretty_assertions = { workspace = true } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/types/Cargo.toml b/types/Cargo.toml index 8ac6b25f7..29887bdb9 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -18,9 +18,9 @@ json = ["hex", "dep:cuprate-helper"] hex = ["dep:hex"] [dependencies] -cuprate-epee-encoding = { path = "../net/epee-encoding", optional = true } -cuprate-helper = { path = "../helper", optional = true, features = ["cast"] } -cuprate-fixed-bytes = { path = "../net/fixed-bytes" } +cuprate-epee-encoding = { workspace = true, optional = true, features = ["std"] } +cuprate-helper = { workspace = true, optional = true, features = ["cast"] } +cuprate-fixed-bytes = { workspace = true } bytes = { workspace = true } curve25519-dalek = { workspace = true } @@ -39,4 +39,4 @@ pretty_assertions = { workspace = true } serde_json = { workspace = true, features = ["std"] } [lints] -workspace = true \ No newline at end of file +workspace = true