diff --git a/Cargo.lock b/Cargo.lock index 074ada5c25..cf4a0dec15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1333,12 +1333,13 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.11" -source = "git+https://github.com/subspace/blst?rev=ab042e18cb3b62e131423380513964e4b2c7b445#ab042e18cb3b62e131423380513964e4b2c7b445" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" dependencies = [ "cc", "glob", - "rayon", + "threadpool", "zeroize", ] @@ -5482,10 +5483,14 @@ dependencies = [ [[package]] name = "kzg" version = "0.1.0" -source = "git+https://github.com/sifraitech/rust-kzg?rev=c34b73916af9b8a699a74bd0186f82f25e72861c#c34b73916af9b8a699a74bd0186f82f25e72861c" +source = "git+https://github.com/grandinetech/rust-kzg?rev=6c8fcc623df3d7e8c0f30951a49bfea764f90bf4#6c8fcc623df3d7e8c0f30951a49bfea764f90bf4" dependencies = [ "blst", + "num_cpus", + "rayon", "sha2 0.10.8", + "siphasher 1.0.1", + "threadpool", ] [[package]] @@ -8188,7 +8193,7 @@ dependencies = [ "memmap2 0.5.10", "parking_lot 0.12.3", "rand", - "siphasher", + "siphasher 0.3.11", "snap", "winapi", ] @@ -9555,13 +9560,12 @@ dependencies = [ [[package]] name = "rust-kzg-blst" version = "0.1.0" -source = "git+https://github.com/sifraitech/rust-kzg?rev=c34b73916af9b8a699a74bd0186f82f25e72861c#c34b73916af9b8a699a74bd0186f82f25e72861c" +source = "git+https://github.com/grandinetech/rust-kzg?rev=6c8fcc623df3d7e8c0f30951a49bfea764f90bf4#6c8fcc623df3d7e8c0f30951a49bfea764f90bf4" dependencies = [ "blst", "hex", "kzg", "libc", - "num_cpus", "once_cell", "rand", "rayon", @@ -11343,6 +11347,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -12535,6 +12545,7 @@ name = "subspace-core-primitives" version = "0.1.0" dependencies = [ "blake3", + "blst", "bytes", "criterion", "derive_more 1.0.0", diff --git a/Cargo.toml b/Cargo.toml index 5b29d30be4..f982230963 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,8 +141,3 @@ sp-weights = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d staging-xcm = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } substrate-prometheus-endpoint = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } xcm-procedural = { git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } - -# TODO: Importing https://github.com/supranational/blst/pull/203 to take advantage of optimizations introduced there, -# switch to upstream once merged or once similar performance improvements land upstream -[patch."https://github.com/supranational/blst.git"] -blst = { git = "https://github.com/subspace/blst", rev = "ab042e18cb3b62e131423380513964e4b2c7b445" } diff --git a/crates/subspace-core-primitives/Cargo.toml b/crates/subspace-core-primitives/Cargo.toml index dea355ccf3..fd2d9fd033 100644 --- a/crates/subspace-core-primitives/Cargo.toml +++ b/crates/subspace-core-primitives/Cargo.toml @@ -17,15 +17,17 @@ bench = false [dependencies] blake3 = { version = "1.5.3", default-features = false } +# TODO: Remove once we switch to big-endian +blst = "0.3.13" bytes = { version = "1.7.1", default-features = false } derive_more = { version = "1.0.0", default-features = false, features = ["full"] } hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -kzg = { git = "https://github.com/sifraitech/rust-kzg", rev = "c34b73916af9b8a699a74bd0186f82f25e72861c", default-features = false } +kzg = { git = "https://github.com/grandinetech/rust-kzg", rev = "6c8fcc623df3d7e8c0f30951a49bfea764f90bf4", default-features = false } num-traits = { version = "0.2.18", default-features = false } parity-scale-codec = { version = "3.6.12", default-features = false, features = ["bytes", "derive", "max-encoded-len"] } parking_lot = { version = "0.12.2", optional = true } rayon = { version = "1.10.0", optional = true } -rust-kzg-blst = { git = "https://github.com/sifraitech/rust-kzg", rev = "c34b73916af9b8a699a74bd0186f82f25e72861c", default-features = false } +rust-kzg-blst = { git = "https://github.com/grandinetech/rust-kzg", rev = "6c8fcc623df3d7e8c0f30951a49bfea764f90bf4", default-features = false } scale-info = { version = "2.11.2", default-features = false, features = ["derive"] } serde = { version = "1.0.206", optional = true, features = ["alloc", "derive"] } serde_arrays = { version = "0.1.0", optional = true } diff --git a/crates/subspace-core-primitives/src/crypto.rs b/crates/subspace-core-primitives/src/crypto.rs index a4ca67164c..14f24b8e9c 100644 --- a/crates/subspace-core-primitives/src/crypto.rs +++ b/crates/subspace-core-primitives/src/crypto.rs @@ -21,12 +21,13 @@ extern crate alloc; pub mod kzg; use crate::Blake3Hash; -use ::kzg::Fr; #[cfg(not(feature = "std"))] use alloc::format; #[cfg(not(feature = "std"))] use alloc::string::String; #[cfg(not(feature = "std"))] +use alloc::string::ToString; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; @@ -207,14 +208,36 @@ impl TryFrom<[u8; Self::FULL_BYTES]> for Scalar { #[inline] fn try_from(value: [u8; Self::FULL_BYTES]) -> Result { - FsFr::from_bytes(&value).map(Scalar) + // TODO: The whole method should have been just the following line, but upstream `rust-kzg` + // switched to big-endian and we have to maintain little-endian version for now + // FsFr::from_bytes(&value).map(Scalar) + let mut bls_scalar = blst::blst_scalar::default(); + let mut fr = blst::blst_fr::default(); + unsafe { + blst::blst_scalar_from_lendian(&mut bls_scalar, value.as_ptr()); + if !blst::blst_scalar_fr_check(&bls_scalar) { + return Err("Invalid scalar".to_string()); + } + blst::blst_fr_from_scalar(&mut fr, &bls_scalar); + } + Ok(Self(FsFr(fr))) } } impl From<&Scalar> for [u8; Scalar::FULL_BYTES] { #[inline] fn from(value: &Scalar) -> Self { - value.0.to_bytes() + // TODO: The whole method should have been just the following line, but upstream `rust-kzg` + // switched to big-endian and we have to maintain little-endian version for now + // value.0.to_bytes() + let mut scalar = blst::blst_scalar::default(); + let mut bytes = [0u8; 32]; + unsafe { + blst::blst_scalar_from_fr(&mut scalar, &value.0 .0); + blst::blst_lendian_from_scalar(bytes.as_mut_ptr(), &scalar); + } + + bytes } } diff --git a/crates/subspace-core-primitives/src/crypto/kzg.rs b/crates/subspace-core-primitives/src/crypto/kzg.rs index 246798ad6d..41a332bcb4 100644 --- a/crates/subspace-core-primitives/src/crypto/kzg.rs +++ b/crates/subspace-core-primitives/src/crypto/kzg.rs @@ -29,7 +29,7 @@ use spin::Mutex; use tracing::debug; /// Embedded KZG settings as bytes, too big for `no_std` in most cases -/// Generated with with following command (using current Ethereum KZG Summoning Ceremony): +/// Generated using following command (using current Ethereum KZG Summoning Ceremony): /// ```bash /// curl -s https://seq.ceremony.ethereum.org/info/current_state | jq '.transcripts[3].powersOfTau' | jq -r '.G1Powers + .G2Powers | map(.[2:]) | join("")' | xxd -r -p - eth-public-parameters.bin /// ``` @@ -72,10 +72,13 @@ pub fn bytes_to_kzg_settings( // Below is the same as `FsKZGSettings::new(&s1, &s2, num_g1_powers, &fft_settings)`, but without // extra checks (parameters are static anyway) and without unnecessary allocations + // TODO: Switch to `::new()` constructor once + // https://github.com/grandinetech/rust-kzg/issues/264 is resolved Ok(FsKZGSettings { fs: fft_settings, secret_g1, secret_g2, + precomputation: None, }) } diff --git a/crates/subspace-core-primitives/src/lib.rs b/crates/subspace-core-primitives/src/lib.rs index 0e2145b61f..ea24f2dfeb 100644 --- a/crates/subspace-core-primitives/src/lib.rs +++ b/crates/subspace-core-primitives/src/lib.rs @@ -164,7 +164,7 @@ pub type SlotNumber = u64; pub type SolutionRange = u64; /// Computes the following: -/// ``` +/// ```text /// MAX * slot_probability / (pieces_in_sector * chunks / s_buckets) / sectors /// ``` pub const fn sectors_to_solution_range( @@ -183,7 +183,7 @@ pub const fn sectors_to_solution_range( } /// Computes the following: -/// ``` +/// ```text /// MAX * slot_probability / (pieces_in_sector * chunks / s_buckets) / solution_range /// ``` pub const fn solution_range_to_sectors( @@ -274,8 +274,10 @@ impl Default for PosProof { } impl PosProof { + /// Constant K used for proof of space + pub const K: u8 = 20; /// Size of proof of space proof in bytes. - pub const SIZE: usize = 20 * 8; + pub const SIZE: usize = Self::K as usize * 8; /// Proof hash. pub fn hash(&self) -> Blake3Hash { diff --git a/crates/subspace-erasure-coding/Cargo.toml b/crates/subspace-erasure-coding/Cargo.toml index 07ae2fe501..1a16abbf17 100644 --- a/crates/subspace-erasure-coding/Cargo.toml +++ b/crates/subspace-erasure-coding/Cargo.toml @@ -15,12 +15,12 @@ include = [ bench = false [dependencies] -kzg = { git = "https://github.com/sifraitech/rust-kzg", rev = "c34b73916af9b8a699a74bd0186f82f25e72861c", default-features = false } -rust-kzg-blst = { git = "https://github.com/sifraitech/rust-kzg", rev = "c34b73916af9b8a699a74bd0186f82f25e72861c", default-features = false } +kzg = { git = "https://github.com/grandinetech/rust-kzg", rev = "6c8fcc623df3d7e8c0f30951a49bfea764f90bf4", default-features = false } +rust-kzg-blst = { git = "https://github.com/grandinetech/rust-kzg", rev = "6c8fcc623df3d7e8c0f30951a49bfea764f90bf4", default-features = false } subspace-core-primitives = { version = "0.1.0", path = "../subspace-core-primitives", default-features = false } [dev-dependencies] -rust-kzg-blst = { git = "https://github.com/sifraitech/rust-kzg", rev = "c34b73916af9b8a699a74bd0186f82f25e72861c" } +rust-kzg-blst = { git = "https://github.com/grandinetech/rust-kzg", rev = "6c8fcc623df3d7e8c0f30951a49bfea764f90bf4" } criterion = "0.5.1" rand = "0.8.5" diff --git a/crates/subspace-farmer/src/plotter.rs b/crates/subspace-farmer/src/plotter.rs index f333488380..0dbf17fcf9 100644 --- a/crates/subspace-farmer/src/plotter.rs +++ b/crates/subspace-farmer/src/plotter.rs @@ -6,6 +6,7 @@ //! implementations without the rest of the library being aware of implementation details. pub mod cpu; +pub mod pool; use async_trait::async_trait; use futures::channel::mpsc; diff --git a/crates/subspace-farmer/src/plotter/cpu.rs b/crates/subspace-farmer/src/plotter/cpu.rs index e4b380b015..5a953eaba2 100644 --- a/crates/subspace-farmer/src/plotter/cpu.rs +++ b/crates/subspace-farmer/src/plotter/cpu.rs @@ -354,28 +354,6 @@ where metrics.plotting_capacity_used.inc(); } - let plotting_fn = || { - let mut sector = Vec::new(); - - let plotted_sector = encode_sector( - downloaded_sector, - EncodeSectorOptions { - sector_index, - sector_output: &mut sector, - records_encoder: &mut CpuRecordsEncoder::::new( - &mut (0..record_encoding_concurrency.get()) - .map(|_| PosTable::generator()) - .collect::>(), - &erasure_coding, - &global_mutex, - ), - abort_early: &abort_early, - }, - )?; - - Ok((sector, plotted_sector)) - }; - let thread_pool = if replotting { &thread_pools.replotting } else { @@ -400,8 +378,31 @@ where let encoding_start = Instant::now(); - let plotting_result = - tokio::task::block_in_place(|| thread_pool.install(plotting_fn)); + let plotting_result = tokio::task::block_in_place(|| { + thread_pool.install(|| { + let mut sector = Vec::new(); + let mut generators = (0..record_encoding_concurrency.get()) + .map(|_| PosTable::generator()) + .collect::>(); + let mut records_encoder = CpuRecordsEncoder::::new( + &mut generators, + &erasure_coding, + &global_mutex, + ); + + let plotted_sector = encode_sector( + downloaded_sector, + EncodeSectorOptions { + sector_index, + sector_output: &mut sector, + records_encoder: &mut records_encoder, + abort_early: &abort_early, + }, + )?; + + Ok((sector, plotted_sector)) + }) + }); drop(thread_pools); if let Some(metrics) = &metrics { metrics.plotting_capacity_used.dec(); diff --git a/crates/subspace-farmer/src/plotter/pool.rs b/crates/subspace-farmer/src/plotter/pool.rs new file mode 100644 index 0000000000..d22a22b322 --- /dev/null +++ b/crates/subspace-farmer/src/plotter/pool.rs @@ -0,0 +1,117 @@ +//! Pool plotter + +use crate::plotter::{Plotter, SectorPlottingProgress}; +use async_trait::async_trait; +use futures::channel::mpsc; +use std::any::type_name_of_val; +use std::time::Duration; +use subspace_core_primitives::{PublicKey, SectorIndex}; +use subspace_farmer_components::FarmerProtocolInfo; +use tracing::{error, trace}; + +/// Pool plotter. +/// +/// This plotter implementation relies on retries and should only be used with local plotter +/// implementations (like CPU and GPU). +#[derive(Debug)] +pub struct AggregatePlotter { + plotters: Vec>, + retry_interval: Duration, +} + +#[async_trait] +impl Plotter for AggregatePlotter { + async fn has_free_capacity(&self) -> Result { + for (index, plotter) in self.plotters.iter().enumerate() { + match plotter.has_free_capacity().await { + Ok(result) => { + if result { + return Ok(true); + } + } + Err(error) => { + error!( + %error, + %index, + r#type = type_name_of_val(plotter), + "Failed to check free capacity for plotter" + ); + } + } + } + + Ok(false) + } + + async fn plot_sector( + &self, + public_key: PublicKey, + sector_index: SectorIndex, + farmer_protocol_info: FarmerProtocolInfo, + pieces_in_sector: u16, + replotting: bool, + progress_sender: mpsc::Sender, + ) { + loop { + for plotter in &self.plotters { + if plotter + .try_plot_sector( + public_key, + sector_index, + farmer_protocol_info, + pieces_in_sector, + replotting, + progress_sender.clone(), + ) + .await + { + return; + } + } + + trace!( + retry_interval = ?self.retry_interval, + "All plotters are busy, will wait and try again later" + ); + tokio::time::sleep(self.retry_interval).await; + } + } + + async fn try_plot_sector( + &self, + public_key: PublicKey, + sector_index: SectorIndex, + farmer_protocol_info: FarmerProtocolInfo, + pieces_in_sector: u16, + replotting: bool, + progress_sender: mpsc::Sender, + ) -> bool { + for plotter in &self.plotters { + if plotter + .try_plot_sector( + public_key, + sector_index, + farmer_protocol_info, + pieces_in_sector, + replotting, + progress_sender.clone(), + ) + .await + { + return true; + } + } + + false + } +} + +impl AggregatePlotter { + /// Create new instance + pub fn new(plotters: Vec>, retry_interval: Duration) -> Self { + Self { + plotters, + retry_interval, + } + } +} diff --git a/crates/subspace-proof-of-space/src/chia.rs b/crates/subspace-proof-of-space/src/chia.rs index 3a090e8f4e..0fb0418a4c 100644 --- a/crates/subspace-proof-of-space/src/chia.rs +++ b/crates/subspace-proof-of-space/src/chia.rs @@ -4,7 +4,7 @@ use crate::{PosTableType, Table, TableGenerator}; use core::mem; use subspace_core_primitives::{PosProof, PosSeed}; -const K: u8 = 20; +const K: u8 = PosProof::K; /// Subspace proof of space table generator. /// diff --git a/crates/subspace-proof-of-space/src/chia_legacy.rs b/crates/subspace-proof-of-space/src/chia_legacy.rs index 20788c7544..c81609a3b9 100644 --- a/crates/subspace-proof-of-space/src/chia_legacy.rs +++ b/crates/subspace-proof-of-space/src/chia_legacy.rs @@ -4,7 +4,7 @@ use crate::{PosTableType, Table, TableGenerator}; use core::mem; use subspace_core_primitives::{PosProof, PosSeed}; -const K: u8 = 20; +const K: u8 = PosProof::K; /// Subspace proof of space table generator. ///