From 72b07b8d04fe946a1a5d21e7ffb856b4cb60e595 Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi <7549475+vedhavyas@users.noreply.github.com> Date: Tue, 30 Jun 2026 17:47:43 +0530 Subject: [PATCH 1/8] Bump nightly to 2026-05-03 Latest nightly that still supports generic_const_exprs. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d58759e7ea8..9ce5833632c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2026-04-11" +channel = "nightly-2026-05-03" components = ["rust-src"] targets = ["wasm32v1-none"] profile = "default" From 67b1b41e701b80b9f75c0a7022ddb02b79bf8ac1 Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi <7549475+vedhavyas@users.noreply.github.com> Date: Wed, 1 Jul 2026 21:22:29 +0530 Subject: [PATCH 2/8] add abundance-backed proof of space Pull abundance's ab-proof-of-space as a pinned, alloc/parallel-gated git dependency, so the no_std runtime never links it. chia_v2::ChiaV2Table wraps its chiapos Tables, looking proofs up under Subspace's s-bucket convention (find_proof with challenge.to_le_bytes()) so they verify under the existing ChiaTable verifier; verification delegates to ChiaTable. Bumps chacha20 0.10.0 -> 0.10.1 (API-only) to satisfy abundance. --- Cargo.lock | 462 ++++++++++++------ Cargo.toml | 4 +- crates/subspace-proof-of-space/Cargo.toml | 5 + crates/subspace-proof-of-space/src/chia_v2.rs | 65 +++ crates/subspace-proof-of-space/src/lib.rs | 2 + 5 files changed, 383 insertions(+), 155 deletions(-) create mode 100644 crates/subspace-proof-of-space/src/chia_v2.rs diff --git a/Cargo.lock b/Cargo.lock index d2791a57ce0..05f0c5016f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,12 +12,87 @@ dependencies = [ "regex", ] +[[package]] +name = "ab-blake3" +version = "0.1.0" +source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +dependencies = [ + "blake3", +] + [[package]] name = "ab-chacha8" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78928d0e8969c1b539a98de5cf3a678d136f160415c765f3f4c464b5eee39976" +[[package]] +name = "ab-chacha8" +version = "0.1.0" +source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" + +[[package]] +name = "ab-core-primitives" +version = "0.1.0" +source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +dependencies = [ + "ab-blake3", + "ab-io-type", + "ab-merkle-tree", + "bech32", + "blake3", + "derive_more 2.1.1", + "ed25519-dalek 3.0.0-rc.1", + "hex", + "rclite", + "replace_with", + "serde-big-array", + "thiserror 2.0.18", + "yoke", +] + +[[package]] +name = "ab-io-type" +version = "0.1.0" +source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +dependencies = [ + "ab-io-type-derive", +] + +[[package]] +name = "ab-io-type-derive" +version = "0.1.0" +source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "ab-merkle-tree" +version = "0.1.0" +source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +dependencies = [ + "ab-blake3", +] + +[[package]] +name = "ab-proof-of-space" +version = "0.1.0" +source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +dependencies = [ + "ab-blake3", + "ab-chacha8 0.1.0 (git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e)", + "ab-core-primitives", + "chacha20 0.10.1", + "derive_more 2.1.1", + "rayon", + "rclite", + "seq-macro", + "sha2 0.11.0", +] + [[package]] name = "actix-codec" version = "0.5.2" @@ -73,7 +148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -187,7 +262,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -356,7 +431,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -367,7 +442,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -396,7 +471,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -547,7 +622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -573,7 +648,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -648,7 +723,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -755,7 +830,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure 0.13.2", ] @@ -767,7 +842,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -953,7 +1028,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1114,7 +1189,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1201,6 +1276,12 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bech32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" + [[package]] name = "binary-merkle-tree" version = "16.1.0" @@ -1448,6 +1529,15 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "branches" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e426eb5cc1900033930ec955317b302e68f19f326cc7bb0c8a86865a826cdf0c" +dependencies = [ + "rustc_version", +] + [[package]] name = "bs58" version = "0.5.1" @@ -1646,9 +1736,9 @@ dependencies = [ [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", "cipher 0.5.1", @@ -1785,7 +1875,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2468,7 +2558,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2574,12 +2664,27 @@ dependencies = [ "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest 0.10.7", - "fiat-crypto", + "fiat-crypto 0.2.9", "rustc_version", "subtle 2.6.1", "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "5.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c906a87e53a36ff795d72e06e8162a83c5436e3ea89e942a9cb9fc083f0a384f" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto 0.3.0", + "rustc_version", + "subtle 2.6.1", +] + [[package]] name = "curve25519-dalek-derive" version = "0.1.1" @@ -2588,7 +2693,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2618,7 +2723,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2632,7 +2737,7 @@ dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2650,7 +2755,7 @@ dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2684,7 +2789,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2697,7 +2802,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2708,7 +2813,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2719,7 +2824,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2824,7 +2929,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2835,7 +2940,7 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2848,7 +2953,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2877,7 +2982,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2890,7 +2995,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.118", "unicode-xid", ] @@ -2994,7 +3099,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3018,7 +3123,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.117", + "syn 2.0.118", "termcolor", "toml 0.8.23", "walkdir", @@ -3447,7 +3552,7 @@ checksum = "7e8671d54058979a37a26f3511fbf8d198ba1aa35ffb202c42587d918d77213a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3467,7 +3572,7 @@ dependencies = [ "elliptic-curve", "rfc6979", "serdect", - "signature", + "signature 2.2.0", "spki", ] @@ -3478,7 +3583,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", - "signature", + "signature 2.2.0", +] + +[[package]] +name = "ed25519" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" +dependencies = [ + "signature 3.0.0", ] [[package]] @@ -3487,24 +3601,36 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ - "curve25519-dalek", - "ed25519", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", "rand_core 0.6.4", "serde", "sha2 0.10.9", - "signature", + "signature 2.2.0", "subtle 2.6.1", "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "3.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1685663e23882cd8517dcbcb1c23a6ebff4433c22dfb681d760219b62cd1b849" +dependencies = [ + "curve25519-dalek 5.0.0-rc.1", + "ed25519 3.0.0", + "sha2 0.11.0", + "subtle 2.6.1", +] + [[package]] name = "ed25519-zebra" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775765289f7c6336c18d3d66127527820dd45ffd9eb3b6b8ee4708590e6c20f5" dependencies = [ - "curve25519-dalek", - "ed25519", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", "hashbrown 0.16.1", "pkcs8", "rand_core 0.6.4", @@ -3522,7 +3648,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3587,7 +3713,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3627,7 +3753,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3682,7 +3808,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3968,7 +4094,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4004,7 +4130,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4184,6 +4310,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + [[package]] name = "file-guard" version = "0.2.0" @@ -4517,7 +4649,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4639,7 +4771,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d69071449e988d6e63e4122c)", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4651,7 +4783,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4661,7 +4793,7 @@ source = "git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d690 dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4841,7 +4973,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5839,7 +5971,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5955,7 +6087,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6066,7 +6198,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6090,7 +6222,7 @@ checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6136,7 +6268,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6164,7 +6296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6264,7 +6396,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6614,7 +6746,7 @@ version = "0.2.14" source = "git+https://github.com/autonomys/rust-libp2p?rev=676c687c2f7a6e92f8f4f77a6934022aec3ba16c#676c687c2f7a6e92f8f4f77a6934022aec3ba16c" dependencies = [ "bs58", - "ed25519-dalek", + "ed25519-dalek 2.2.0", "hkdf", "multihash 0.19.5", "prost 0.14.3", @@ -6807,7 +6939,7 @@ source = "git+https://github.com/autonomys/rust-libp2p?rev=676c687c2f7a6e92f8f4f dependencies = [ "heck 0.5.0", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7041,7 +7173,7 @@ dependencies = [ "bs58", "bytes", "cid", - "ed25519-dalek", + "ed25519-dalek 2.2.0", "enum-display", "futures", "futures-timer", @@ -7163,7 +7295,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7177,7 +7309,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7188,7 +7320,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7199,7 +7331,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7210,7 +7342,7 @@ checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7342,7 +7474,7 @@ dependencies = [ "bitflags 1.3.2", "blake2 0.10.6", "c2-chacha", - "curve25519-dalek", + "curve25519-dalek 4.1.3", "either", "hashlink 0.8.4", "lioness", @@ -7413,7 +7545,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7663,8 +7795,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879011babc47a1c7fdf5a935ae3cfe94f34645ca0cac1c7f6424b36fc743d1bf" dependencies = [ "data-encoding", - "ed25519", - "ed25519-dalek", + "ed25519 2.2.3", + "ed25519-dalek 2.2.0", "getrandom 0.2.17", "log", "rand 0.8.6", @@ -7735,7 +7867,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7866,7 +7998,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7999,7 +8131,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -8792,7 +8924,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -8944,7 +9076,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9011,7 +9143,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9397,7 +9529,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9407,7 +9539,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a4f5352e13c1ca5f0e4d7b4a804fbb85b0e02c45cae435d101fe71081bc8ed8" dependencies = [ "polkavm-derive-impl", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9555,7 +9687,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d69071449e988d6e63e4122c)", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9602,7 +9734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9698,7 +9830,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9709,7 +9841,7 @@ checksum = "75eea531cfcd120e0851a3f8aed42c4841f78c889eefafd96339c72677ae42c3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9729,7 +9861,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "version_check", "yansi", ] @@ -9768,7 +9900,7 @@ checksum = "9adf1691c04c0a5ff46ff8f262b58beb07b0dbb61f96f9f54f6cbd82106ed87f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9835,7 +9967,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "itertools 0.14.0", "log", "multimap", @@ -9845,7 +9977,7 @@ dependencies = [ "prost 0.13.5", "prost-types 0.13.5", "regex", - "syn 2.0.117", + "syn 2.0.118", "tempfile", ] @@ -9855,7 +9987,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "itertools 0.14.0", "log", "multimap", @@ -9864,7 +9996,7 @@ dependencies = [ "prost 0.14.3", "prost-types 0.14.3", "regex", - "syn 2.0.117", + "syn 2.0.118", "tempfile", ] @@ -9890,7 +10022,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9903,7 +10035,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9916,7 +10048,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9957,7 +10089,7 @@ checksum = "8bccae89ed67a40989e780105fab43e6c71a077b9fc8ae4c805ff5f73d2a79c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -10039,9 +10171,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -10091,7 +10223,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "chacha20 0.10.0", + "chacha20 0.10.1", "getrandom 0.4.2", "rand_core 0.10.1", ] @@ -10216,6 +10348,15 @@ dependencies = [ "yasna 0.5.2", ] +[[package]] +name = "rclite" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6716a637a8a17bce72da6b6ddab8548619cc748658c41ab6ee5c4a3c1001385" +dependencies = [ + "branches", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -10271,7 +10412,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -10336,6 +10477,12 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "replace_with" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884" + [[package]] name = "reqwest" version = "0.12.28" @@ -10432,7 +10579,7 @@ checksum = "652db34deaaa57929e10ca18e5454a32cb0efc351ae80d320334bbf907b908b3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -10569,7 +10716,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10828,7 +10975,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -11410,7 +11557,7 @@ source = "git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d690 dependencies = [ "bs58", "bytes", - "ed25519-dalek", + "ed25519-dalek 2.2.0", "libp2p-identity", "libp2p-kad", "litep2p", @@ -11828,7 +11975,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -11931,7 +12078,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -11959,7 +12106,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -11985,7 +12132,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -12007,7 +12154,7 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.117", + "syn 2.0.118", "thiserror 2.0.18", ] @@ -12059,7 +12206,7 @@ dependencies = [ "aead", "arrayref", "arrayvec 0.7.6", - "curve25519-dalek", + "curve25519-dalek 4.1.3", "getrandom_or_panic", "merlin", "rand_core 0.6.4", @@ -12288,7 +12435,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -12321,7 +12468,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -12369,7 +12516,7 @@ dependencies = [ "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -12487,7 +12634,7 @@ checksum = "c1e303f8205714074f6068773f0e29527e0453937fe837c9717d066635b65f31" dependencies = [ "pkcs8", "rand_core 0.6.4", - "signature", + "signature 2.2.0", "zeroize", ] @@ -12501,6 +12648,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" + [[package]] name = "simba" version = "0.9.1" @@ -12706,7 +12859,7 @@ dependencies = [ "aes-gcm", "blake2 0.10.6", "chacha20poly1305", - "curve25519-dalek", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", "ring", "rustc_version", @@ -12731,7 +12884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -12783,7 +12936,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -13082,7 +13235,7 @@ source = "git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d690 dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d69071449e988d6e63e4122c)", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -13101,7 +13254,7 @@ source = "git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d690 dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -13289,7 +13442,7 @@ source = "git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d690 dependencies = [ "bytes", "docify", - "ed25519-dalek", + "ed25519-dalek 2.2.0", "libsecp256k1", "log", "parity-scale-codec", @@ -13524,7 +13677,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -13580,8 +13733,8 @@ version = "24.0.0" source = "git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d69071449e988d6e63e4122c#8304f8852018c9b2d69071449e988d6e63e4122c" dependencies = [ "aes-gcm", - "curve25519-dalek", - "ed25519-dalek", + "curve25519-dalek 4.1.3", + "ed25519-dalek 2.2.0", "hkdf", "parity-scale-codec", "rand 0.8.6", @@ -13732,7 +13885,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -13977,7 +14130,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -13989,7 +14142,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -14474,9 +14627,10 @@ dependencies = [ name = "subspace-proof-of-space" version = "0.1.0" dependencies = [ - "ab-chacha8", + "ab-chacha8 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ab-proof-of-space", "blake3", - "chacha20 0.10.0", + "chacha20 0.10.1", "criterion", "derive_more 2.1.1", "hex", @@ -15038,7 +15192,7 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.117", + "syn 2.0.118", "thiserror 2.0.18", ] @@ -15103,7 +15257,7 @@ dependencies = [ "subxt-codegen", "subxt-metadata", "subxt-utils-fetchmetadata", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -15207,9 +15361,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -15245,7 +15399,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -15323,7 +15477,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -15342,7 +15496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -15377,7 +15531,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -15388,7 +15542,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -15550,7 +15704,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -15802,7 +15956,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -15845,7 +15999,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -16378,7 +16532,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] @@ -16778,7 +16932,7 @@ checksum = "a9cdb9c2e3965ee15629d067203cb800e9822664d04335dadc6fe1788d4fc335" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -16904,7 +17058,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -17005,7 +17159,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -17016,7 +17170,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -17418,7 +17572,7 @@ dependencies = [ "heck 0.5.0", "indexmap", "prettyplease", - "syn 2.0.117", + "syn 2.0.118", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -17434,7 +17588,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -17497,7 +17651,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", "serde", "zeroize", @@ -17556,7 +17710,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -17634,9 +17788,9 @@ checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -17645,13 +17799,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure 0.13.2", ] @@ -17672,7 +17826,7 @@ checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -17692,7 +17846,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure 0.13.2", ] @@ -17713,7 +17867,7 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -17746,7 +17900,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0e11af7f745..16b56e394e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ members = [ [workspace.dependencies] ab-chacha8 = "0.1.0" +ab-proof-of-space = { git = "https://github.com/nazar-pc/abundance", rev = "fc8da9992575a3b33ea9c5c764ea71f926f4592e", default-features = false } actix-web = { version = "4.9.0", default-features = false } aes = "0.9.0" anyhow = "1.0.89" @@ -51,7 +52,7 @@ blst = "0.3.13" bytes = { version = "1.11.1", default-features = false } bytesize = "2.3.1" cc = "1.2.62" -chacha20 = { version = "0.10.0", default-features = false } +chacha20 = { version = "0.10.1", default-features = false } clap = "4.6.1" console = "0.15.11" core_affinity = "0.8.1" @@ -318,6 +319,7 @@ ziggy = { version = "1.7.0", default-features = false } # # This list is ordered alphabetically. [profile.dev.package] +ab-proof-of-space = { opt-level = 3 } bitvec = { opt-level = 3 } blake2 = { opt-level = 3 } blake3 = { opt-level = 3 } diff --git a/crates/subspace-proof-of-space/Cargo.toml b/crates/subspace-proof-of-space/Cargo.toml index 7fe5239d108..a903e083133 100644 --- a/crates/subspace-proof-of-space/Cargo.toml +++ b/crates/subspace-proof-of-space/Cargo.toml @@ -19,6 +19,7 @@ bench = false [dependencies] ab-chacha8.workspace = true +ab-proof-of-space = { workspace = true, optional = true } blake3 = { workspace = true, default-features = false } chacha20 = { workspace = true, features = ["cipher"], optional = true } derive_more = { workspace = true, features = ["full"] } @@ -44,6 +45,9 @@ default = [] alloc = [ "dep:chacha20", "dep:seq-macro", + "dep:ab-proof-of-space", + "ab-proof-of-space/alloc", + "ab-proof-of-space/full-chiapos", ] # Enabling this feature exposes quality search on `chiapos` module as well as enables support for K=15..=25 (by default # only K=20 is exposed) @@ -53,4 +57,5 @@ full-chiapos = [ parallel = [ "alloc", "dep:rayon", + "ab-proof-of-space/parallel", ] diff --git a/crates/subspace-proof-of-space/src/chia_v2.rs b/crates/subspace-proof-of-space/src/chia_v2.rs new file mode 100644 index 00000000000..c8e3fd54e6c --- /dev/null +++ b/crates/subspace-proof-of-space/src/chia_v2.rs @@ -0,0 +1,65 @@ +//! Chia proof of space backed by abundance's `ab-proof-of-space`. +//! +//! Proofs are looked up under Subspace's s-bucket convention, so they verify under the existing +//! [`ChiaTable`] verifier. + +use crate::chia::ChiaTable; +use crate::{PosTableType, Table, TableGenerator}; +use ab_proof_of_space::chiapos::{Tables, TablesCache}; +use subspace_core_primitives::pos::{PosProof, PosSeed}; +use subspace_core_primitives::solutions::SolutionPotVerifier; + +const K: u8 = PosProof::K; + +/// Proof of space table generator. +/// +/// Chia implementation. +#[derive(Debug, Default, Clone)] +pub struct ChiaV2TableGenerator { + tables_cache: TablesCache, +} + +impl TableGenerator for ChiaV2TableGenerator { + fn generate(&self, seed: &PosSeed) -> ChiaV2Table { + ChiaV2Table { + tables: Tables::::create((*seed).into(), &self.tables_cache), + } + } + + #[cfg(feature = "parallel")] + fn generate_parallel(&self, seed: &PosSeed) -> ChiaV2Table { + ChiaV2Table { + tables: Tables::::create_parallel((*seed).into(), &self.tables_cache), + } + } +} + +/// Proof of space table. +/// +/// Chia implementation. +#[derive(Debug)] +pub struct ChiaV2Table { + tables: Tables, +} + +impl SolutionPotVerifier for ChiaV2Table { + fn is_proof_valid(seed: &PosSeed, challenge_index: u32, proof: &PosProof) -> bool { + ::is_proof_valid(seed, challenge_index, proof) + } +} + +impl Table for ChiaV2Table { + const TABLE_TYPE: PosTableType = PosTableType::Chia; + type Generator = ChiaV2TableGenerator; + + fn find_proof(&self, challenge_index: u32) -> Option { + self.tables + .find_proof(challenge_index.to_le_bytes()) + .next() + .map(PosProof::from) + } + + fn is_proof_valid(seed: &PosSeed, challenge_index: u32, proof: &PosProof) -> bool { + ::is_proof_valid(seed, challenge_index, proof) + } +} diff --git a/crates/subspace-proof-of-space/src/lib.rs b/crates/subspace-proof-of-space/src/lib.rs index ed99a725d0b..fceb1354f2d 100644 --- a/crates/subspace-proof-of-space/src/lib.rs +++ b/crates/subspace-proof-of-space/src/lib.rs @@ -8,6 +8,8 @@ #![cfg_attr(feature = "parallel", feature(exact_size_is_empty, sync_unsafe_cell))] pub mod chia; +#[cfg(feature = "alloc")] +pub mod chia_v2; pub mod chiapos; pub mod shim; From bb844729b9d5214a405e4197a8460a1f5c32b324 Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi <7549475+vedhavyas@users.noreply.github.com> Date: Wed, 1 Jul 2026 09:20:02 +0530 Subject: [PATCH 3/8] add farm cutover point with in-place v0 upgrade PlotMetadataHeader gains a cutover field (Option) and a typed PlotMetadataVersion (V0/V1); a version-aware Decode reads old V0 headers as cutover = None and rejects unknown versions (the old-binary lockout). Opening a V0 plot records the cutover as the highest history size among existing sectors and rewrites the header as V1. New-sector plotting then waits until history advances past the cutover, so no sector is stamped at history_size <= cutover and later misread with the old proof-of-space. Sectors at or below the cutover use the old proof-of-space, newer ones the new; nothing dispatches on it yet. --- .../subspace-farmer/src/single_disk_farm.rs | 91 ++++++++++++------- .../src/single_disk_farm/plotting.rs | 17 ++++ 2 files changed, 77 insertions(+), 31 deletions(-) diff --git a/crates/subspace-farmer/src/single_disk_farm.rs b/crates/subspace-farmer/src/single_disk_farm.rs index f3184bddf9c..ea535668421 100644 --- a/crates/subspace-farmer/src/single_disk_farm.rs +++ b/crates/subspace-farmer/src/single_disk_farm.rs @@ -255,24 +255,70 @@ pub enum SingleDiskFarmSummary { }, } -#[derive(Debug, Encode, Decode)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)] +enum PlotMetadataVersion { + /// Original layout, before the proof-of-space cutover. + #[codec(index = 0)] + V0, + /// Adds the proof-of-space `cutover`. + #[codec(index = 1)] + V1, +} + +impl PlotMetadataVersion { + /// Latest version this implementation writes. + const LATEST: Self = Self::V1; +} + +#[derive(Debug, Encode)] struct PlotMetadataHeader { - version: u8, + version: PlotMetadataVersion, plotted_sector_count: SectorIndex, + /// History size of the newest sector plotted before the proof-of-space cutover, or `None` for + /// a farm with no pre-cutover sectors. Sectors with `history_size <= cutover` use the old + /// proof-of-space, newer ones the new one. Present only in version 1 and later. + cutover: Option, } impl PlotMetadataHeader { #[inline] fn encoded_size() -> usize { let default = PlotMetadataHeader { - version: 0, + version: PlotMetadataVersion::LATEST, plotted_sector_count: 0, + // `Some` is the larger encoding; size the header buffer for it so a version 1 header + // with a recorded cutover always fits. + cutover: Some(HistorySize::from(SegmentIndex::ZERO)), }; default.encoded_size() } } +// TODO(ved): Drop this manual `Decode` and return to `#[derive(Decode)]` once the `cutover` field +// is removed after the proof-of-space migration completes. It exists only to decode version 0 +// headers that predate the `cutover` field. +impl Decode for PlotMetadataHeader { + fn decode( + input: &mut I, + ) -> Result { + // Rejects unknown (future) versions — this is the old-binary lockout. + let version = PlotMetadataVersion::decode(input)?; + let plotted_sector_count = SectorIndex::decode(input)?; + // `cutover` was added in version 1; version 0 headers do not carry it. + let cutover = match version { + PlotMetadataVersion::V0 => None, + PlotMetadataVersion::V1 => Option::::decode(input)?, + }; + + Ok(Self { + version, + plotted_sector_count, + cutover, + }) + } +} + /// Options used to open single disk farm #[derive(Debug)] pub struct SingleDiskFarmOptions<'a, NC> @@ -388,9 +434,6 @@ pub enum SingleDiskFarmError { /// Failed to decode metadata header #[error("Failed to decode metadata header: {0}")] FailedToDecodeMetadataHeader(parity_scale_codec::Error), - /// Unexpected metadata version - #[error("Unexpected metadata version {0}")] - UnexpectedMetadataVersion(u8), /// Allocated space is not enough for one sector #[error( "Allocated space is not enough for one sector. \ @@ -528,9 +571,6 @@ pub enum SingleDiskFarmScrubError { /// Failed to decode metadata header #[error("Failed to decode metadata header: {0}")] FailedToDecodeMetadataHeader(parity_scale_codec::Error), - /// Unexpected metadata version - #[error("Unexpected metadata version {0}")] - UnexpectedMetadataVersion(u8), /// Cache can't be opened #[error("Cache at {file} can't be opened: {error}")] CacheCantBeOpened { @@ -821,7 +861,6 @@ impl SingleDiskFarm { pub const PLOT_FILE: &'static str = "plot.bin"; /// Name of the metadata file pub const METADATA_FILE: &'static str = "metadata.bin"; - const SUPPORTED_PLOT_VERSION: u8 = 0; /// Create new single disk farm instance pub async fn new( @@ -1350,10 +1389,11 @@ impl SingleDiskFarm { // Align plot file size for disk sector size let expected_metadata_size = expected_metadata_size.div_ceil(DISK_SECTOR_SIZE as u64) * DISK_SECTOR_SIZE as u64; - let metadata_header = if metadata_size == 0 { + let mut metadata_header = if metadata_size == 0 { let metadata_header = PlotMetadataHeader { - version: SingleDiskFarm::SUPPORTED_PLOT_VERSION, + version: PlotMetadataVersion::LATEST, plotted_sector_count: 0, + cutover: None, }; metadata_file @@ -1380,12 +1420,6 @@ impl SingleDiskFarm { PlotMetadataHeader::decode(&mut metadata_header_bytes.as_ref()) .map_err(SingleDiskFarmError::FailedToDecodeMetadataHeader)?; - if metadata_header.version != SingleDiskFarm::SUPPORTED_PLOT_VERSION { - return Err(SingleDiskFarmError::UnexpectedMetadataVersion( - metadata_header.version, - )); - } - if metadata_header.plotted_sector_count > target_sector_count { metadata_header.plotted_sector_count = target_sector_count; metadata_file.write_all_at(&metadata_header.encode(), 0)?; @@ -1430,6 +1464,14 @@ impl SingleDiskFarm { sectors_metadata.push(sector_metadata); } + // Upgrade a version-0 plot in place: its sectors predate the new proof-of-space, so set + // the cutover from their max history size (not node history, which can trail on sync). + if metadata_header.version < PlotMetadataVersion::LATEST { + metadata_header.cutover = sectors_metadata.iter().map(|m| m.history_size).max(); + metadata_header.version = PlotMetadataVersion::LATEST; + metadata_file.write_all_at(&metadata_header.encode(), 0)?; + } + Arc::new(AsyncRwLock::new(sectors_metadata)) }; @@ -1589,13 +1631,6 @@ impl SingleDiskFarm { io::Error::other(format!("Failed to decode metadata header: {error}")) })?; - if metadata_header.version != SingleDiskFarm::SUPPORTED_PLOT_VERSION { - return Err(io::Error::other(format!( - "Unsupported metadata version {}", - metadata_header.version - ))); - } - let mut sectors_metadata = Vec::::with_capacity( ((metadata_size - RESERVED_PLOT_METADATA) / sector_metadata_size as u64) as usize, ); @@ -1863,12 +1898,6 @@ impl SingleDiskFarm { .map_err(SingleDiskFarmScrubError::FailedToDecodeMetadataHeader)? }; - if metadata_header.version != SingleDiskFarm::SUPPORTED_PLOT_VERSION { - return Err(SingleDiskFarmScrubError::UnexpectedMetadataVersion( - metadata_header.version, - )); - } - let plotted_sector_count = metadata_header.plotted_sector_count; let expected_metadata_size = RESERVED_PLOT_METADATA diff --git a/crates/subspace-farmer/src/single_disk_farm/plotting.rs b/crates/subspace-farmer/src/single_disk_farm/plotting.rs index 9fa6d5e03fa..9d4ed0b76d9 100644 --- a/crates/subspace-farmer/src/single_disk_farm/plotting.rs +++ b/crates/subspace-farmer/src/single_disk_farm/plotting.rs @@ -125,6 +125,7 @@ where } = plotting_options; let sector_plotting_options = §or_plotting_options; + let cutover = metadata_header.cutover; let plotting_semaphore = Semaphore::new(max_plotting_sectors_per_farm.get()); let mut sectors_being_plotted = FuturesOrdered::new(); // Channel size is intentionally unbounded for easier analysis, but it is bounded by plotting @@ -165,6 +166,7 @@ where sectors_metadata, sectors_being_modified, &plotting_semaphore, + cutover, ) .instrument(info_span!("", %sector_index)) .fuse(); @@ -287,6 +289,7 @@ async fn plot_single_sector<'a, NC>( sectors_metadata: &'a AsyncRwLock>, sectors_being_modified: &'a AsyncRwLock>, plotting_semaphore: &'a Semaphore, + cutover: Option, ) -> PlotSingleSectorResult< impl Future, PlottingError>> + 'a, > @@ -384,6 +387,20 @@ where } } + // Wait for history to pass the cutover before plotting, so a new sector isn't stamped at or + // below it and later read back with the old proof-of-space. + if let Some(cutover) = cutover + && farmer_app_info.protocol_info.history_size <= cutover + { + debug!( + current_history_size = %farmer_app_info.protocol_info.history_size, + %cutover, + "History size has not advanced past the proof-of-space cutover yet, waiting" + ); + tokio::time::sleep(FARMER_APP_INFO_RETRY_INTERVAL).await; + continue; + } + break farmer_app_info; }; From 38090130668aeeb4029b4143bfeb5e04cdcbfdcd Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi <7549475+vedhavyas@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:21:48 +0530 Subject: [PATCH 4/8] dispatch proof-of-space per sector by history size PosTable (subspace-proof-of-space, alloc-gated) dispatches between the old ChiaTable and the new ChiaV2Table; both proofs verify under ChiaTable, so the choice is a farmer-side read/prove concern. The Table trait gains generator_for(is_post_cutover) and SolutionCandidates a history_size() accessor; the farmer picks per sector by comparing history_size against the farm's cutover at reading, proving, and auditing. New sectors always use the new implementation. --- .../subspace-farmer-components/src/proving.rs | 6 ++ .../bin/subspace-farmer/commands/benchmark.rs | 63 ++++++------- .../src/bin/subspace-farmer/main.rs | 3 +- .../subspace-farmer/src/single_disk_farm.rs | 23 ++++- .../src/single_disk_farm/farming.rs | 45 ++++------ .../src/single_disk_farm/piece_reader.rs | 18 +++- crates/subspace-proof-of-space/src/lib.rs | 12 +++ .../subspace-proof-of-space/src/pos_table.rs | 90 +++++++++++++++++++ 8 files changed, 192 insertions(+), 68 deletions(-) create mode 100644 crates/subspace-proof-of-space/src/pos_table.rs diff --git a/crates/subspace-farmer-components/src/proving.rs b/crates/subspace-farmer-components/src/proving.rs index 1a8400f26ae..8b88a9c88ab 100644 --- a/crates/subspace-farmer-components/src/proving.rs +++ b/crates/subspace-farmer-components/src/proving.rs @@ -17,6 +17,7 @@ use std::io; use subspace_core_primitives::pieces::{PieceOffset, Record}; use subspace_core_primitives::pos::PosSeed; use subspace_core_primitives::sectors::{SBucket, SectorId}; +use subspace_core_primitives::segments::HistorySize; use subspace_core_primitives::solutions::{ChunkWitness, Solution, SolutionRange}; use subspace_core_primitives::{PublicKey, ScalarBytes}; use subspace_erasure_coding::ErasureCoding; @@ -159,6 +160,11 @@ where self.chunk_candidates.is_empty() } + /// History size of the sector these candidates belong to. + pub fn history_size(&self) -> HistorySize { + self.sector_metadata.history_size + } + /// Turn solution candidates into actual solutions pub fn into_solutions( self, diff --git a/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs b/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs index 8e96fbbc03b..451bf955927 100644 --- a/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs +++ b/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs @@ -2,7 +2,6 @@ use crate::PosTable; use anyhow::anyhow; use clap::{Parser, Subcommand}; use criterion::{BatchSize, Criterion, Throughput}; -use parking_lot::Mutex; use rayon::{ThreadPool, ThreadPoolBuildError, ThreadPoolBuilder}; use std::collections::HashSet; use std::fs::OpenOptions; @@ -162,9 +161,7 @@ where .expect("Not zero; qed"), ) .map_err(|error| anyhow!("Failed to instantiate erasure coding: {error}"))?; - let table_generator = Mutex::new(PosTable::generator()); - - let sectors_metadata = SingleDiskFarm::read_all_sectors_metadata(&disk_farm) + let (cutover, sectors_metadata) = SingleDiskFarm::read_all_sectors_metadata(&disk_farm) .map_err(|error| anyhow::anyhow!("Failed to read sectors metadata: {error}"))?; let mut criterion = Criterion::default().sample_size(sample_size); @@ -187,7 +184,7 @@ where b.iter_batched( rand::random::<[u8; 32]>, |global_challenge| { - let options = PlotAuditOptions:: { + let options = PlotAuditOptions { public_key: single_disk_farm_info.public_key(), reward_address: single_disk_farm_info.public_key(), slot_info: SlotInfo { @@ -204,10 +201,10 @@ where sectors_being_modified: &HashSet::default(), read_sector_record_chunks_mode: ReadSectorRecordChunksMode::ConcurrentChunks, - table_generator: &table_generator, + cutover, }; - black_box(plot_audit.audit(black_box(options))) + black_box(plot_audit.audit::(black_box(options))) }, BatchSize::SmallInput, ) @@ -224,7 +221,7 @@ where b.iter_batched( rand::random::<[u8; 32]>, |global_challenge| { - let options = PlotAuditOptions:: { + let options = PlotAuditOptions { public_key: single_disk_farm_info.public_key(), reward_address: single_disk_farm_info.public_key(), slot_info: SlotInfo { @@ -241,10 +238,10 @@ where sectors_being_modified: &HashSet::default(), read_sector_record_chunks_mode: ReadSectorRecordChunksMode::ConcurrentChunks, - table_generator: &table_generator, + cutover, }; - black_box(plot_audit.audit(black_box(options))) + black_box(plot_audit.audit::(black_box(options))) }, BatchSize::SmallInput, ) @@ -259,7 +256,7 @@ where b.iter_batched( rand::random::<[u8; 32]>, |global_challenge| { - let options = PlotAuditOptions:: { + let options = PlotAuditOptions { public_key: single_disk_farm_info.public_key(), reward_address: single_disk_farm_info.public_key(), slot_info: SlotInfo { @@ -276,10 +273,10 @@ where sectors_being_modified: &HashSet::default(), read_sector_record_chunks_mode: ReadSectorRecordChunksMode::ConcurrentChunks, - table_generator: &table_generator, + cutover, }; - black_box(plot_audit.audit(black_box(options))) + black_box(plot_audit.audit::(black_box(options))) }, BatchSize::SmallInput, ) @@ -342,9 +339,7 @@ where .expect("Not zero; qed"), ) .map_err(|error| anyhow!("Failed to instantiate erasure coding: {error}"))?; - let table_generator = Mutex::new(PosTable::generator()); - - let mut sectors_metadata = SingleDiskFarm::read_all_sectors_metadata(&disk_farm) + let (cutover, mut sectors_metadata) = SingleDiskFarm::read_all_sectors_metadata(&disk_farm) .map_err(|error| anyhow::anyhow!("Failed to read sectors metadata: {error}"))?; if let Some(limit_sector_count) = limit_sector_count { sectors_metadata.truncate(limit_sector_count); @@ -362,7 +357,7 @@ where .open(disk_farm.join(SingleDiskFarm::PLOT_FILE)) .map_err(|error| anyhow::anyhow!("Failed to open plot: {error}"))?; let plot_audit = PlotAudit::new(&plot); - let mut options = PlotAuditOptions:: { + let mut options = PlotAuditOptions { public_key: single_disk_farm_info.public_key(), reward_address: single_disk_farm_info.public_key(), slot_info: SlotInfo { @@ -378,10 +373,10 @@ where erasure_coding: &erasure_coding, sectors_being_modified: &HashSet::default(), read_sector_record_chunks_mode: ReadSectorRecordChunksMode::ConcurrentChunks, - table_generator: &table_generator, + cutover, }; - let mut audit_results = plot_audit.audit(options).unwrap(); + let mut audit_results = plot_audit.audit::(options).unwrap(); group.bench_function("plot/single/concurrent-chunks", |b| { b.iter_batched( @@ -392,7 +387,7 @@ where options.slot_info.global_challenge = Blake3Hash::from(rand::random::<[u8; 32]>()); - audit_results = plot_audit.audit(options).unwrap(); + audit_results = plot_audit.audit::(options).unwrap(); audit_results.pop().unwrap() }, @@ -406,7 +401,7 @@ where }); options.read_sector_record_chunks_mode = ReadSectorRecordChunksMode::WholeSector; - let mut audit_results = plot_audit.audit(options).unwrap(); + let mut audit_results = plot_audit.audit::(options).unwrap(); group.bench_function("plot/single/whole-sector", |b| { b.iter_batched( @@ -417,7 +412,7 @@ where options.slot_info.global_challenge = Blake3Hash::from(rand::random::<[u8; 32]>()); - audit_results = plot_audit.audit(options).unwrap(); + audit_results = plot_audit.audit::(options).unwrap(); audit_results.pop().unwrap() }, @@ -436,7 +431,7 @@ where }) .map_err(|error| anyhow::anyhow!("Failed to open plot: {error}"))?; let plot_audit = PlotAudit::new(&plot); - let mut options = PlotAuditOptions:: { + let mut options = PlotAuditOptions { public_key: single_disk_farm_info.public_key(), reward_address: single_disk_farm_info.public_key(), slot_info: SlotInfo { @@ -452,10 +447,10 @@ where erasure_coding: &erasure_coding, sectors_being_modified: &HashSet::default(), read_sector_record_chunks_mode: ReadSectorRecordChunksMode::ConcurrentChunks, - table_generator: &table_generator, + cutover, }; - let mut audit_results = plot_audit.audit(options).unwrap(); + let mut audit_results = plot_audit.audit::(options).unwrap(); group.bench_function("plot/rayon/unbuffered/concurrent-chunks", |b| { b.iter_batched( @@ -466,7 +461,7 @@ where options.slot_info.global_challenge = Blake3Hash::from(rand::random::<[u8; 32]>()); - audit_results = plot_audit.audit(options).unwrap(); + audit_results = plot_audit.audit::(options).unwrap(); audit_results.pop().unwrap() }, @@ -480,7 +475,7 @@ where }); options.read_sector_record_chunks_mode = ReadSectorRecordChunksMode::WholeSector; - let mut audit_results = plot_audit.audit(options).unwrap(); + let mut audit_results = plot_audit.audit::(options).unwrap(); group.bench_function("plot/rayon/unbuffered/whole-sector", |b| { b.iter_batched( @@ -491,7 +486,7 @@ where options.slot_info.global_challenge = Blake3Hash::from(rand::random::<[u8; 32]>()); - audit_results = plot_audit.audit(options).unwrap(); + audit_results = plot_audit.audit::(options).unwrap(); audit_results.pop().unwrap() }, @@ -508,7 +503,7 @@ where let plot = RayonFiles::open(disk_farm.join(SingleDiskFarm::PLOT_FILE)) .map_err(|error| anyhow::anyhow!("Failed to open plot: {error}"))?; let plot_audit = PlotAudit::new(&plot); - let mut options = PlotAuditOptions:: { + let mut options = PlotAuditOptions { public_key: single_disk_farm_info.public_key(), reward_address: single_disk_farm_info.public_key(), slot_info: SlotInfo { @@ -524,10 +519,10 @@ where erasure_coding: &erasure_coding, sectors_being_modified: &HashSet::default(), read_sector_record_chunks_mode: ReadSectorRecordChunksMode::ConcurrentChunks, - table_generator: &table_generator, + cutover, }; - let mut audit_results = plot_audit.audit(options).unwrap(); + let mut audit_results = plot_audit.audit::(options).unwrap(); group.bench_function("plot/rayon/regular/concurrent-chunks", |b| { b.iter_batched( @@ -538,7 +533,7 @@ where options.slot_info.global_challenge = Blake3Hash::from(rand::random::<[u8; 32]>()); - audit_results = plot_audit.audit(options).unwrap(); + audit_results = plot_audit.audit::(options).unwrap(); audit_results.pop().unwrap() }, @@ -552,7 +547,7 @@ where }); options.read_sector_record_chunks_mode = ReadSectorRecordChunksMode::WholeSector; - let mut audit_results = plot_audit.audit(options).unwrap(); + let mut audit_results = plot_audit.audit::(options).unwrap(); group.bench_function("plot/rayon/regular/whole-sector", |b| { b.iter_batched( @@ -563,7 +558,7 @@ where options.slot_info.global_challenge = Blake3Hash::from(rand::random::<[u8; 32]>()); - audit_results = plot_audit.audit(options).unwrap(); + audit_results = plot_audit.audit::(options).unwrap(); audit_results.pop().unwrap() }, diff --git a/crates/subspace-farmer/src/bin/subspace-farmer/main.rs b/crates/subspace-farmer/src/bin/subspace-farmer/main.rs index 2d185e3401b..5d53e6fa774 100644 --- a/crates/subspace-farmer/src/bin/subspace-farmer/main.rs +++ b/crates/subspace-farmer/src/bin/subspace-farmer/main.rs @@ -5,13 +5,12 @@ use std::fs; use std::path::PathBuf; use subspace_farmer::single_disk_farm::{ScrubTarget, SingleDiskFarm}; use subspace_process::{init_logger, raise_fd_limit, set_exit_on_panic}; -use subspace_proof_of_space::chia::ChiaTable; use tracing::info; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; -type PosTable = ChiaTable; +type PosTable = subspace_proof_of_space::PosTable; #[allow(clippy::large_enum_variant)] #[derive(Debug, Parser)] diff --git a/crates/subspace-farmer/src/single_disk_farm.rs b/crates/subspace-farmer/src/single_disk_farm.rs index ea535668421..e53eb793a04 100644 --- a/crates/subspace-farmer/src/single_disk_farm.rs +++ b/crates/subspace-farmer/src/single_disk_farm.rs @@ -319,6 +319,11 @@ impl Decode for PlotMetadataHeader { } } +/// With no cutover (a fresh farm) every sector is post-cutover. +fn is_post_cutover(cutover: Option, history_size: HistorySize) -> bool { + cutover.is_none_or(|cutover| history_size > cutover) +} + /// Options used to open single disk farm #[derive(Debug)] pub struct SingleDiskFarmOptions<'a, NC> @@ -1009,6 +1014,9 @@ impl SingleDiskFarm { let (farming_plot, farming_thread_pool) = AsyncJoinOnDrop::new(farming_plot_fut, false).await??; + // The plot's cutover decides which proof-of-space each sector reads and proves with. + let cutover = metadata_header.cutover; + let plotting_join_handle = task::spawn_blocking({ let sectors_metadata = Arc::clone(§ors_metadata); let handlers = Arc::clone(&handlers); @@ -1155,6 +1163,7 @@ impl SingleDiskFarm { read_sector_record_chunks_mode, global_mutex, metrics, + cutover, }; farming::(farming_options).await }; @@ -1198,6 +1207,7 @@ impl SingleDiskFarm { erasure_coding, sectors_being_modified, read_sector_record_chunks_mode, + cutover, global_mutex, ); @@ -1614,10 +1624,10 @@ impl SingleDiskFarm { Ok(effective_disk_usage) } - /// Read all sectors metadata + /// Read the proof-of-space cutover and all sectors metadata pub fn read_all_sectors_metadata( directory: &Path, - ) -> io::Result> { + ) -> io::Result<(Option, Vec)> { let metadata_file = DirectIoFile::open(directory.join(Self::METADATA_FILE))?; let metadata_size = metadata_file.size()?; @@ -1648,7 +1658,14 @@ impl SingleDiskFarm { ); } - Ok(sectors_metadata) + // A version-0 plot has no stored cutover; derive it from its sectors, like the upgrade. + let cutover = if metadata_header.version < PlotMetadataVersion::LATEST { + sectors_metadata.iter().map(|m| m.history_size).max() + } else { + metadata_header.cutover + }; + + Ok((cutover, sectors_metadata)) } /// ID of this farm diff --git a/crates/subspace-farmer/src/single_disk_farm/farming.rs b/crates/subspace-farmer/src/single_disk_farm/farming.rs index 73cf9782093..21b5b1abd54 100644 --- a/crates/subspace-farmer/src/single_disk_farm/farming.rs +++ b/crates/subspace-farmer/src/single_disk_farm/farming.rs @@ -14,7 +14,6 @@ use crate::single_disk_farm::metrics::SingleDiskFarmMetrics; use async_lock::{Mutex as AsyncMutex, RwLock as AsyncRwLock}; use futures::StreamExt; use futures::channel::mpsc; -use parking_lot::Mutex; use rayon::ThreadPool; use std::collections::HashSet; use std::sync::Arc; @@ -73,11 +72,8 @@ where } /// Plot audit options -#[derive(Debug)] -pub struct PlotAuditOptions<'a, 'b, PosTable> -where - PosTable: Table, -{ +#[derive(Debug, Clone, Copy)] +pub struct PlotAuditOptions<'a, 'b> { /// Public key of the farm pub public_key: &'a PublicKey, /// Reward address to use for solutions @@ -95,22 +91,11 @@ where pub sectors_being_modified: &'b HashSet, /// Mode of reading chunks during proving pub read_sector_record_chunks_mode: ReadSectorRecordChunksMode, - /// Proof of space table generator - pub table_generator: &'a Mutex, -} - -impl Clone for PlotAuditOptions<'_, '_, PosTable> -where - PosTable: Table, -{ - #[inline] - fn clone(&self) -> Self { - *self - } + /// Proof-of-space cutover: sectors with a history size above this use the new implementation, + /// `None` on a farm with no pre-cutover sectors. + pub cutover: Option, } -impl Copy for PlotAuditOptions<'_, '_, PosTable> where PosTable: Table {} - /// Plot auditing implementation #[derive(Debug)] pub struct PlotAudit(Plot) @@ -130,7 +115,7 @@ where #[allow(clippy::type_complexity)] pub fn audit<'b, PosTable>( &'a self, - options: PlotAuditOptions<'a, 'b, PosTable>, + options: PlotAuditOptions<'a, 'b>, ) -> Result< Vec<( SectorIndex, @@ -151,7 +136,7 @@ where erasure_coding, sectors_being_modified, read_sector_record_chunks_mode: mode, - table_generator, + cutover, } = options; let audit_results = audit_plot_sync( @@ -168,12 +153,17 @@ where .filter_map(|audit_results| { let sector_index = audit_results.sector_index; - let sector_solutions = audit_results.solution_candidates.into_solutions( + let solution_candidates = audit_results.solution_candidates; + let is_post_cutover = + super::is_post_cutover(cutover, solution_candidates.history_size()); + let table_generator = PosTable::generator_for(is_post_cutover); + + let sector_solutions = solution_candidates.into_solutions( reward_address, kzg, erasure_coding, mode, - |seed: &PosSeed| table_generator.lock().generate_parallel(seed), + move |seed: &PosSeed| table_generator.generate_parallel(seed), ); let sector_solutions = match sector_solutions { @@ -214,6 +204,7 @@ pub(super) struct FarmingOptions { pub(super) read_sector_record_chunks_mode: ReadSectorRecordChunksMode, pub(super) global_mutex: Arc>, pub(super) metrics: Option>, + pub(super) cutover: Option, } /// Starts farming process. @@ -243,6 +234,7 @@ where read_sector_record_chunks_mode, global_mutex, metrics, + cutover, } = farming_options; let farmer_app_info = node_client @@ -253,7 +245,6 @@ where // We assume that each slot is one second let farming_timeout = farmer_app_info.farming_timeout; - let table_generator = Arc::new(Mutex::new(PosTable::generator())); let span = Span::current(); let mut non_fatal_errors = 0; @@ -278,7 +269,7 @@ where .install(|| { let _span_guard = span.enter(); - plot_audit.audit(PlotAuditOptions:: { + plot_audit.audit::(PlotAuditOptions { public_key: &public_key, reward_address: &reward_address, slot_info, @@ -287,7 +278,7 @@ where erasure_coding: &erasure_coding, sectors_being_modified, read_sector_record_chunks_mode, - table_generator: &table_generator, + cutover, }) }) .map_err(FarmingError::LowLevelAuditing)? diff --git a/crates/subspace-farmer/src/single_disk_farm/piece_reader.rs b/crates/subspace-farmer/src/single_disk_farm/piece_reader.rs index b67d0de69b2..07c594af5e0 100644 --- a/crates/subspace-farmer/src/single_disk_farm/piece_reader.rs +++ b/crates/subspace-farmer/src/single_disk_farm/piece_reader.rs @@ -12,6 +12,7 @@ use std::sync::Arc; use subspace_core_primitives::PublicKey; use subspace_core_primitives::pieces::{Piece, PieceOffset}; use subspace_core_primitives::sectors::{SectorId, SectorIndex}; +use subspace_core_primitives::segments::HistorySize; use subspace_erasure_coding::ErasureCoding; use subspace_farmer_components::reading::ReadSectorRecordChunksMode; use subspace_farmer_components::sector::{SectorMetadataChecksummed, sector_size}; @@ -58,6 +59,7 @@ impl DiskPieceReader { erasure_coding: ErasureCoding, sectors_being_modified: Arc>>, read_sector_record_chunks_mode: ReadSectorRecordChunksMode, + cutover: Option, global_mutex: Arc>, ) -> (Self, impl Future) where @@ -75,6 +77,7 @@ impl DiskPieceReader { sectors_being_modified, read_piece_receiver, read_sector_record_chunks_mode, + cutover, global_mutex, ) .await @@ -118,12 +121,16 @@ async fn read_pieces( sectors_being_modified: Arc>>, mut read_piece_receiver: mpsc::Receiver, mode: ReadSectorRecordChunksMode, + cutover: Option, global_mutex: Arc>, ) where PosTable: Table, S: ReadAtSync, { - let mut table_generator = PosTable::generator(); + // Keep a warm generator for each proof-of-space so old and new sectors can be read without + // re-allocating table caches per request. + let mut table_generator_old = PosTable::generator_for(false); + let mut table_generator_new = PosTable::generator_for(true); while let Some(read_piece_request) = read_piece_receiver.next().await { let ReadPieceRequest { @@ -194,6 +201,13 @@ async fn read_pieces( // Take mutex briefly to make sure piece reading is allowed right now global_mutex.lock().await; + let is_post_cutover = super::is_post_cutover(cutover, sector_metadata.history_size); + let table_generator = if is_post_cutover { + &mut table_generator_new + } else { + &mut table_generator_old + }; + let maybe_piece = read_piece::( &public_key, piece_offset, @@ -202,7 +216,7 @@ async fn read_pieces( &ReadAt::from_sync(§or), &erasure_coding, mode, - &mut table_generator, + table_generator, ) .await; diff --git a/crates/subspace-proof-of-space/src/lib.rs b/crates/subspace-proof-of-space/src/lib.rs index fceb1354f2d..30c27bc60b7 100644 --- a/crates/subspace-proof-of-space/src/lib.rs +++ b/crates/subspace-proof-of-space/src/lib.rs @@ -11,8 +11,13 @@ pub mod chia; #[cfg(feature = "alloc")] pub mod chia_v2; pub mod chiapos; +#[cfg(feature = "alloc")] +pub mod pos_table; pub mod shim; +#[cfg(feature = "alloc")] +pub use pos_table::{PosTable, PosTableGenerator}; + #[cfg(feature = "alloc")] extern crate alloc; @@ -72,4 +77,11 @@ pub trait Table: SolutionPotVerifier + Sized + Send + Sync + 'static { fn generator() -> Self::Generator { Self::Generator::default() } + + /// Table generator for a sector, given whether it was plotted after the cutover. Single-impl + /// tables ignore the argument. + #[cfg(feature = "alloc")] + fn generator_for(_is_post_cutover: bool) -> Self::Generator { + Self::generator() + } } diff --git a/crates/subspace-proof-of-space/src/pos_table.rs b/crates/subspace-proof-of-space/src/pos_table.rs new file mode 100644 index 00000000000..8b010c37e0f --- /dev/null +++ b/crates/subspace-proof-of-space/src/pos_table.rs @@ -0,0 +1,90 @@ +//! Proof of space that dispatches per sector between the old [`ChiaTable`] and the new +//! [`ChiaV2Table`], so one farm can mix them across the migration cutover. + +use crate::chia::ChiaTable; +use crate::chia_v2::ChiaV2Table; +use crate::{PosTableType, Table, TableGenerator}; +use subspace_core_primitives::pos::{PosProof, PosSeed}; +use subspace_core_primitives::solutions::SolutionPotVerifier; + +/// Proof of space table generator dispatching between the old and new Chia implementations. +#[derive(Debug, Clone)] +pub enum PosTableGenerator { + /// Old (pre-cutover) implementation. + V1(::Generator), + /// New (post-cutover) implementation. + V2(::Generator), +} + +impl Default for PosTableGenerator { + #[inline] + fn default() -> Self { + Self::V2(ChiaV2Table::generator()) + } +} + +impl PosTableGenerator { + /// Generator for a sector: the new implementation for post-cutover sectors, the old one + /// otherwise. + #[inline] + pub fn new(is_post_cutover: bool) -> Self { + if is_post_cutover { + Self::V2(ChiaV2Table::generator()) + } else { + Self::V1(ChiaTable::generator()) + } + } +} + +impl TableGenerator for PosTableGenerator { + fn generate(&self, seed: &PosSeed) -> PosTable { + match self { + Self::V1(generator) => PosTable::V1(generator.generate(seed)), + Self::V2(generator) => PosTable::V2(generator.generate(seed)), + } + } + + #[cfg(feature = "parallel")] + fn generate_parallel(&self, seed: &PosSeed) -> PosTable { + match self { + Self::V1(generator) => PosTable::V1(generator.generate_parallel(seed)), + Self::V2(generator) => PosTable::V2(generator.generate_parallel(seed)), + } + } +} + +/// Proof of space table dispatching between the old and new Chia implementations. +#[derive(Debug)] +pub enum PosTable { + /// Old (pre-cutover) implementation. + V1(ChiaTable), + /// New (post-cutover) implementation. + V2(ChiaV2Table), +} + +impl SolutionPotVerifier for PosTable { + fn is_proof_valid(seed: &PosSeed, challenge_index: u32, proof: &PosProof) -> bool { + // Both V1 and V2 proofs verify under ChiaTable. + ::is_proof_valid(seed, challenge_index, proof) + } +} + +impl Table for PosTable { + const TABLE_TYPE: PosTableType = PosTableType::Chia; + type Generator = PosTableGenerator; + + fn generator_for(is_post_cutover: bool) -> Self::Generator { + PosTableGenerator::new(is_post_cutover) + } + + fn find_proof(&self, challenge_index: u32) -> Option { + match self { + Self::V1(table) => table.find_proof(challenge_index), + Self::V2(table) => table.find_proof(challenge_index), + } + } + + fn is_proof_valid(seed: &PosSeed, challenge_index: u32, proof: &PosProof) -> bool { + ::is_proof_valid(seed, challenge_index, proof) + } +} From 339b2dfec730dc72b834b9b03dd597a5bf7f4ce9 Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi <7549475+vedhavyas@users.noreply.github.com> Date: Wed, 1 Jul 2026 21:25:44 +0530 Subject: [PATCH 5/8] add tests and benches for the abundance proof of space Cross-verify abundance-backed proofs against the existing ChiaTable verifier, a plot->read round-trip, and the per-sector V1/V2 dispatch selection. Point the benches at the new table. --- .../benches/auditing.rs | 4 +- .../benches/plotting.rs | 4 +- .../benches/proving.rs | 4 +- .../benches/reading.rs | 4 +- .../tests/plot_read_roundtrip.rs | 101 ++++++++++++++++++ .../subspace-farmer/src/single_disk_farm.rs | 49 +++++++++ crates/subspace-proof-of-space/benches/pos.rs | 7 +- crates/subspace-proof-of-space/src/chia_v2.rs | 3 + .../src/chia_v2/tests.rs | 29 +++++ .../subspace-proof-of-space/src/pos_table.rs | 3 + .../src/pos_table/tests.rs | 18 ++++ 11 files changed, 215 insertions(+), 11 deletions(-) create mode 100644 crates/subspace-farmer-components/tests/plot_read_roundtrip.rs create mode 100644 crates/subspace-proof-of-space/src/chia_v2/tests.rs create mode 100644 crates/subspace-proof-of-space/src/pos_table/tests.rs diff --git a/crates/subspace-farmer-components/benches/auditing.rs b/crates/subspace-farmer-components/benches/auditing.rs index 434821ea18d..675a13b4784 100644 --- a/crates/subspace-farmer-components/benches/auditing.rs +++ b/crates/subspace-farmer-components/benches/auditing.rs @@ -26,9 +26,9 @@ use subspace_farmer_components::sector::{ }; use subspace_kzg::Kzg; use subspace_proof_of_space::Table; -use subspace_proof_of_space::chia::ChiaTable; +use subspace_proof_of_space::chia_v2::ChiaV2Table; -type PosTable = ChiaTable; +type PosTable = ChiaV2Table; const MAX_PIECES_IN_SECTOR: u16 = 1000; diff --git a/crates/subspace-farmer-components/benches/plotting.rs b/crates/subspace-farmer-components/benches/plotting.rs index 63f828b7d4d..b7a203098d3 100644 --- a/crates/subspace-farmer-components/benches/plotting.rs +++ b/crates/subspace-farmer-components/benches/plotting.rs @@ -14,9 +14,9 @@ use subspace_farmer_components::plotting::{CpuRecordsEncoder, PlotSectorOptions, use subspace_farmer_components::sector::sector_size; use subspace_kzg::Kzg; use subspace_proof_of_space::Table; -use subspace_proof_of_space::chia::ChiaTable; +use subspace_proof_of_space::chia_v2::ChiaV2Table; -type PosTable = ChiaTable; +type PosTable = ChiaV2Table; const MAX_PIECES_IN_SECTOR: u16 = 1000; diff --git a/crates/subspace-farmer-components/benches/proving.rs b/crates/subspace-farmer-components/benches/proving.rs index 00e31e23924..14c0e169863 100644 --- a/crates/subspace-farmer-components/benches/proving.rs +++ b/crates/subspace-farmer-components/benches/proving.rs @@ -31,10 +31,10 @@ use subspace_farmer_components::sector::{ SectorContentsMap, SectorMetadata, SectorMetadataChecksummed, sector_size, }; use subspace_kzg::Kzg; -use subspace_proof_of_space::chia::ChiaTable; +use subspace_proof_of_space::chia_v2::ChiaV2Table; use subspace_proof_of_space::{Table, TableGenerator}; -type PosTable = ChiaTable; +type PosTable = ChiaV2Table; const MAX_PIECES_IN_SECTOR: u16 = 1000; diff --git a/crates/subspace-farmer-components/benches/reading.rs b/crates/subspace-farmer-components/benches/reading.rs index fe660d5de3a..55f639e6883 100644 --- a/crates/subspace-farmer-components/benches/reading.rs +++ b/crates/subspace-farmer-components/benches/reading.rs @@ -25,9 +25,9 @@ use subspace_farmer_components::sector::{ use subspace_farmer_components::{FarmerProtocolInfo, ReadAt, ReadAtSync}; use subspace_kzg::Kzg; use subspace_proof_of_space::Table; -use subspace_proof_of_space::chia::ChiaTable; +use subspace_proof_of_space::chia_v2::ChiaV2Table; -type PosTable = ChiaTable; +type PosTable = ChiaV2Table; const MAX_PIECES_IN_SECTOR: u16 = 1000; diff --git a/crates/subspace-farmer-components/tests/plot_read_roundtrip.rs b/crates/subspace-farmer-components/tests/plot_read_roundtrip.rs new file mode 100644 index 00000000000..399f7b52fb4 --- /dev/null +++ b/crates/subspace-farmer-components/tests/plot_read_roundtrip.rs @@ -0,0 +1,101 @@ +//! Plot a sector with the abundance-backed table and read a piece back unchanged. + +use futures::executor::block_on; +use rand::prelude::*; +use std::num::{NonZeroU64, NonZeroUsize}; +use std::slice; +use subspace_archiving::archiver::Archiver; +use subspace_core_primitives::PublicKey; +use subspace_core_primitives::pieces::{PieceOffset, Record}; +use subspace_core_primitives::segments::{HistorySize, RecordedHistorySegment}; +use subspace_data_retrieval::piece_getter::PieceGetter; +use subspace_erasure_coding::ErasureCoding; +use subspace_farmer_components::plotting::{CpuRecordsEncoder, PlotSectorOptions, plot_sector}; +use subspace_farmer_components::reading::{ReadSectorRecordChunksMode, read_piece}; +use subspace_farmer_components::{FarmerProtocolInfo, ReadAt}; +use subspace_kzg::Kzg; +use subspace_proof_of_space::Table; +use subspace_proof_of_space::chia_v2::ChiaV2Table; + +#[test] +fn abundance_plot_read_roundtrip() { + let pieces_in_sector = 10; + let sector_index = 0; + let public_key = PublicKey::default(); + + let mut input = RecordedHistorySegment::new_boxed(); + StdRng::seed_from_u64(42).fill(AsMut::<[u8]>::as_mut(input.as_mut())); + let kzg = Kzg::new(); + let erasure_coding = ErasureCoding::new( + NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize) + .expect("Not zero; qed"), + ) + .unwrap(); + let mut archiver = Archiver::new(kzg.clone(), erasure_coding.clone()); + let archived_history_segment = archiver + .add_block( + AsRef::<[u8]>::as_ref(input.as_ref()).to_vec(), + Default::default(), + true, + ) + .archived_segments + .into_iter() + .next() + .unwrap(); + + let farmer_protocol_info = FarmerProtocolInfo { + history_size: HistorySize::from(NonZeroU64::new(1).unwrap()), + max_pieces_in_sector: pieces_in_sector, + recent_segments: HistorySize::from(NonZeroU64::new(5).unwrap()), + recent_history_fraction: ( + HistorySize::from(NonZeroU64::new(1).unwrap()), + HistorySize::from(NonZeroU64::new(10).unwrap()), + ), + min_sector_lifetime: HistorySize::from(NonZeroU64::new(4).unwrap()), + }; + + let mut table_generator = ChiaV2Table::generator(); + let mut sector = Vec::new(); + let plotted_sector = block_on(plot_sector(PlotSectorOptions { + public_key: &public_key, + sector_index, + piece_getter: &archived_history_segment, + farmer_protocol_info, + kzg: &kzg, + erasure_coding: &erasure_coding, + pieces_in_sector, + sector_output: &mut sector, + downloading_semaphore: None, + encoding_semaphore: None, + records_encoder: &mut CpuRecordsEncoder::::new( + slice::from_mut(&mut table_generator), + &erasure_coding, + &Default::default(), + ), + abort_early: &Default::default(), + })) + .unwrap(); + + let piece_offset = PieceOffset::ZERO; + let piece_index = plotted_sector.piece_indexes[usize::from(piece_offset)]; + let expected = block_on(archived_history_segment.get_piece(piece_index)) + .unwrap() + .unwrap(); + + let read = block_on(read_piece::( + piece_offset, + &plotted_sector.sector_id, + &plotted_sector.sector_metadata, + &ReadAt::from_sync(sector.as_slice()), + &erasure_coding, + ReadSectorRecordChunksMode::ConcurrentChunks, + &mut table_generator, + )) + .unwrap(); + + assert_eq!( + read.record(), + expected.record(), + "record read back from an abundance-plotted sector must match the original" + ); +} diff --git a/crates/subspace-farmer/src/single_disk_farm.rs b/crates/subspace-farmer/src/single_disk_farm.rs index e53eb793a04..4a204d7b9b1 100644 --- a/crates/subspace-farmer/src/single_disk_farm.rs +++ b/crates/subspace-farmer/src/single_disk_farm.rs @@ -2407,3 +2407,52 @@ fn write_dummy_sector_metadata( error, }) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn plot_metadata_header_version_upgrade() { + // A version 0 header carries no cutover field yet must still decode, with cutover = None. + #[derive(Encode)] + struct V0Header { + version: u8, + plotted_sector_count: SectorIndex, + } + let v0_bytes = V0Header { + version: 0, + plotted_sector_count: 42, + } + .encode(); + let decoded = + PlotMetadataHeader::decode(&mut v0_bytes.as_slice()).expect("version 0 header decodes"); + assert_eq!(decoded.version, PlotMetadataVersion::V0); + assert_eq!(decoded.plotted_sector_count, 42); + assert_eq!(decoded.cutover, None); + + // A current header round-trips with its cutover intact. + let cutover = Some(HistorySize::from(SegmentIndex::ZERO)); + let header = PlotMetadataHeader { + version: PlotMetadataVersion::LATEST, + plotted_sector_count: 7, + cutover, + }; + let decoded = PlotMetadataHeader::decode(&mut header.encode().as_slice()) + .expect("current header round-trips"); + assert_eq!(decoded.version, PlotMetadataVersion::LATEST); + assert_eq!(decoded.plotted_sector_count, 7); + assert_eq!(decoded.cutover, cutover); + } + + #[test] + fn is_post_cutover_boundary() { + let h = |n: u64| HistorySize::from(SegmentIndex::from(n)); + // No cutover: a fresh farm plots everything with the new proof-of-space. + assert!(is_post_cutover(None, h(1))); + // At or below the cutover stays old; strictly above moves to the new proof-of-space. + assert!(!is_post_cutover(Some(h(10)), h(9))); + assert!(!is_post_cutover(Some(h(10)), h(10))); + assert!(is_post_cutover(Some(h(10)), h(11))); + } +} diff --git a/crates/subspace-proof-of-space/benches/pos.rs b/crates/subspace-proof-of-space/benches/pos.rs index 947fce582dc..3516fd869ff 100644 --- a/crates/subspace-proof-of-space/benches/pos.rs +++ b/crates/subspace-proof-of-space/benches/pos.rs @@ -250,13 +250,14 @@ fn pos_bench( } pub fn criterion_benchmark(c: &mut Criterion) { + #[cfg(feature = "alloc")] { // This challenge index with above seed is known to not have a solution - let challenge_index_without_solution = 1232460437; + let challenge_index_without_solution = 0; // This challenge index with above seed is known to have a solution - let challenge_index_with_solution = 600426542; + let challenge_index_with_solution = 3; - pos_bench::( + pos_bench::( c, "chia", challenge_index_without_solution, diff --git a/crates/subspace-proof-of-space/src/chia_v2.rs b/crates/subspace-proof-of-space/src/chia_v2.rs index c8e3fd54e6c..959bde781af 100644 --- a/crates/subspace-proof-of-space/src/chia_v2.rs +++ b/crates/subspace-proof-of-space/src/chia_v2.rs @@ -63,3 +63,6 @@ impl Table for ChiaV2Table { ::is_proof_valid(seed, challenge_index, proof) } } + +#[cfg(test)] +mod tests; diff --git a/crates/subspace-proof-of-space/src/chia_v2/tests.rs b/crates/subspace-proof-of-space/src/chia_v2/tests.rs new file mode 100644 index 00000000000..8ef345cf23d --- /dev/null +++ b/crates/subspace-proof-of-space/src/chia_v2/tests.rs @@ -0,0 +1,29 @@ +use crate::chia::ChiaTable; +use crate::chia_v2::ChiaV2TableGenerator; +use crate::{Table, TableGenerator}; +use subspace_core_primitives::pos::PosSeed; +use subspace_core_primitives::sectors::SBucket; + +// Every abundance-backed proof must verify under ChiaTable::is_proof_valid. +#[test] +fn proofs_verify_under_consensus() { + let generator = ChiaV2TableGenerator::default(); + + for seed_byte in 0..3u8 { + let seed = PosSeed::from([seed_byte; 32]); + let table = generator.generate(&seed); + + let mut verified = 0u32; + for s_bucket in u16::from(SBucket::ZERO)..=u16::from(SBucket::MAX) { + if let Some(proof) = table.find_proof(u32::from(s_bucket)) { + assert!( + ChiaTable::is_proof_valid(&seed, u32::from(s_bucket), &proof), + "seed {seed_byte}: proof for s-bucket {s_bucket} rejected by the consensus \ + verifier" + ); + verified += 1; + } + } + assert!(verified > 0, "seed {seed_byte}: no proofs produced"); + } +} diff --git a/crates/subspace-proof-of-space/src/pos_table.rs b/crates/subspace-proof-of-space/src/pos_table.rs index 8b010c37e0f..caec6a96f0c 100644 --- a/crates/subspace-proof-of-space/src/pos_table.rs +++ b/crates/subspace-proof-of-space/src/pos_table.rs @@ -88,3 +88,6 @@ impl Table for PosTable { ::is_proof_valid(seed, challenge_index, proof) } } + +#[cfg(test)] +mod tests; diff --git a/crates/subspace-proof-of-space/src/pos_table/tests.rs b/crates/subspace-proof-of-space/src/pos_table/tests.rs new file mode 100644 index 00000000000..96527cca084 --- /dev/null +++ b/crates/subspace-proof-of-space/src/pos_table/tests.rs @@ -0,0 +1,18 @@ +use crate::pos_table::PosTableGenerator; + +// New sectors get the abundance-backed table; sectors at or below the cutover keep the old one. +#[test] +fn dispatch_selects_by_cutover() { + assert!(matches!( + PosTableGenerator::new(true), + PosTableGenerator::V2(_) + )); + assert!(matches!( + PosTableGenerator::new(false), + PosTableGenerator::V1(_) + )); + assert!(matches!( + PosTableGenerator::default(), + PosTableGenerator::V2(_) + )); +} From a0ab94784d576ac706b436475159c8836605826c Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi <7549475+vedhavyas@users.noreply.github.com> Date: Fri, 3 Jul 2026 07:50:01 +0530 Subject: [PATCH 6/8] add wgpu GPU plotter backed by abundance proof of space Reworks ChiaV2Table onto abundance's dense proof path and adds a wgpu records encoder (Vulkan/Metal) mirroring the CUDA backend, wired into the farm and cluster plotters behind a wgpu cargo feature. Abundance dependencies are patched to my fork for review. --- Cargo.lock | 705 +++++++++++++++++- Cargo.toml | 10 + crates/subspace-farmer/Cargo.toml | 3 + .../commands/cluster/plotter.rs | 135 ++++ .../src/bin/subspace-farmer/commands/farm.rs | 135 ++++ crates/subspace-farmer/src/plotter/gpu.rs | 2 + .../subspace-farmer/src/plotter/gpu/wgpu.rs | 140 ++++ crates/subspace-proof-of-space/Cargo.toml | 2 + crates/subspace-proof-of-space/src/chia_v2.rs | 23 +- .../subspace-proof-of-space-wgpu/Cargo.toml | 19 + .../subspace-proof-of-space-wgpu/src/lib.rs | 133 ++++ 11 files changed, 1279 insertions(+), 28 deletions(-) create mode 100644 crates/subspace-farmer/src/plotter/gpu/wgpu.rs create mode 100644 shared/subspace-proof-of-space-wgpu/Cargo.toml create mode 100644 shared/subspace-proof-of-space-wgpu/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 05f0c5016f1..8e6cdb51c9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,7 @@ dependencies = [ [[package]] name = "ab-blake3" version = "0.1.0" -source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" dependencies = [ "blake3", ] @@ -29,12 +29,12 @@ checksum = "78928d0e8969c1b539a98de5cf3a678d136f160415c765f3f4c464b5eee39976" [[package]] name = "ab-chacha8" version = "0.1.0" -source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" [[package]] name = "ab-core-primitives" version = "0.1.0" -source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" dependencies = [ "ab-blake3", "ab-io-type", @@ -54,7 +54,7 @@ dependencies = [ [[package]] name = "ab-io-type" version = "0.1.0" -source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" dependencies = [ "ab-io-type-derive", ] @@ -62,7 +62,7 @@ dependencies = [ [[package]] name = "ab-io-type-derive" version = "0.1.0" -source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" dependencies = [ "proc-macro2", "quote", @@ -72,7 +72,7 @@ dependencies = [ [[package]] name = "ab-merkle-tree" version = "0.1.0" -source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" dependencies = [ "ab-blake3", ] @@ -80,10 +80,10 @@ dependencies = [ [[package]] name = "ab-proof-of-space" version = "0.1.0" -source = "git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e#fc8da9992575a3b33ea9c5c764ea71f926f4592e" +source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" dependencies = [ "ab-blake3", - "ab-chacha8 0.1.0 (git+https://github.com/nazar-pc/abundance?rev=fc8da9992575a3b33ea9c5c764ea71f926f4592e)", + "ab-chacha8 0.1.0 (git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47)", "ab-core-primitives", "chacha20 0.10.1", "derive_more 2.1.1", @@ -93,6 +93,29 @@ dependencies = [ "sha2 0.11.0", ] +[[package]] +name = "ab-proof-of-space-gpu" +version = "0.1.0" +source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +dependencies = [ + "ab-blake3", + "ab-chacha8 0.1.0 (git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47)", + "ab-core-primitives", + "ab-proof-of-space", + "anyhow", + "async-lock", + "cargo-gpu-install", + "derive_more 2.1.1", + "futures", + "parking_lot 0.12.5", + "rayon", + "rclite", + "spirv-std", + "thiserror 2.0.18", + "tracing", + "wgpu", +] + [[package]] name = "actix-codec" version = "0.5.2" @@ -447,9 +470,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "approx" @@ -806,6 +829,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + [[package]] name = "asn1-rs" version = "0.7.1" @@ -1348,7 +1380,16 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec", + "bit-vec 0.8.0", +] + +[[package]] +name = "bit-set" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" +dependencies = [ + "bit-vec 0.9.1", ] [[package]] @@ -1357,6 +1398,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" + [[package]] name = "bitcoin-io" version = "0.1.4" @@ -1384,6 +1431,9 @@ name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] [[package]] name = "bitvec" @@ -1482,6 +1532,15 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "blocking" version = "1.6.2" @@ -1583,6 +1642,20 @@ name = "bytemuck" version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] [[package]] name = "byteorder" @@ -1633,6 +1706,19 @@ dependencies = [ "serde_core", ] +[[package]] +name = "cargo-gpu-install" +version = "0.10.0-alpha.1" +source = "git+https://github.com/Rust-GPU/rust-gpu?rev=bd49568baf08301107c9f698494735724980dee0#bd49568baf08301107c9f698494735724980dee0" +dependencies = [ + "anyhow", + "cargo_metadata 0.21.0", + "directories 6.0.0", + "log", + "serde", + "spirv-builder", +] + [[package]] name = "cargo-platform" version = "0.1.9" @@ -1642,6 +1728,31 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo-platform" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84982c6c0ae343635a3a4ee6dedef965513735c8b183caa7289fa6e27399ebd4" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-util-schemas" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830" +dependencies = [ + "semver 1.0.27", + "serde", + "serde-untagged", + "serde-value", + "thiserror 2.0.18", + "toml 0.8.23", + "unicode-xid", + "url", +] + [[package]] name = "cargo_metadata" version = "0.15.4" @@ -1649,13 +1760,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", - "cargo-platform", + "cargo-platform 0.1.9", "semver 1.0.27", "serde", "serde_json", "thiserror 1.0.69", ] +[[package]] +name = "cargo_metadata" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cfca2aaa699835ba88faf58a06342a314a950d2b9686165e038286c30316868" +dependencies = [ + "camino", + "cargo-platform 0.2.0", + "cargo-util-schemas", + "semver 1.0.27", + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "case" version = "1.0.0" @@ -3046,7 +3172,16 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", +] + +[[package]] +name = "directories" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" +dependencies = [ + "dirs-sys 0.5.0", ] [[package]] @@ -3065,7 +3200,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", ] [[package]] @@ -3076,10 +3211,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.59.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -3087,10 +3234,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -3129,6 +3286,15 @@ dependencies = [ "walkdir", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "domain-block-builder" version = "0.1.0" @@ -3801,6 +3967,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + [[package]] name = "errno" version = "0.3.14" @@ -5169,6 +5346,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "glam" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436" +dependencies = [ + "libm", +] + [[package]] name = "glob" version = "0.3.3" @@ -5207,6 +5393,39 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror 2.0.18", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.11.0", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "group" version = "0.13.0" @@ -5245,6 +5464,7 @@ checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "num-traits", "zerocopy", ] @@ -5400,6 +5620,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + [[package]] name = "hickory-net" version = "0.26.1" @@ -6556,6 +6782,16 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libm" version = "0.2.16" @@ -7213,6 +7449,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "local-waker" version = "0.1.4" @@ -7680,6 +7922,33 @@ dependencies = [ "unsigned-varint 0.8.0", ] +[[package]] +name = "naga" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" +dependencies = [ + "arrayvec 0.7.6", + "bit-set 0.9.1", + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases 0.2.1", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "petgraph 0.8.3", + "rustc-hash 1.1.0", + "spirv", + "thiserror 2.0.18", + "unicode-ident", +] + [[package]] name = "nalgebra" version = "0.33.2" @@ -8001,6 +8270,68 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", +] + [[package]] name = "object" version = "0.36.7" @@ -8110,6 +8441,24 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + [[package]] name = "ouroboros" version = "0.18.5" @@ -9727,6 +10076,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "prettyplease" version = "0.2.37" @@ -9866,6 +10221,12 @@ dependencies = [ "yansi", ] +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" + [[package]] name = "prometheus" version = "0.13.4" @@ -9918,8 +10279,8 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ - "bit-set", - "bit-vec", + "bit-set 0.8.0", + "bit-vec 0.8.0", "bitflags 2.11.0", "num-traits", "rand 0.9.4", @@ -10309,6 +10670,30 @@ dependencies = [ "bitflags 2.11.0", ] +[[package]] +name = "raw-string" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0501e134c6905fee1f10fed25b0a7e1261bf676cffac9543a7d0730dec01af2" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-quartz-core", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -10395,6 +10780,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + [[package]] name = "ref-cast" version = "1.0.25" @@ -10477,6 +10873,12 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + [[package]] name = "replace_with" version = "0.1.8" @@ -10608,6 +11010,16 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "rspirv" +version = "0.13.0+sdk-1.4.341.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091dca2e1d6fd3098417b5ec88e77e80d1ba5945750943419dc976858082c296" +dependencies = [ + "rustc-hash 1.1.0", + "spirv", +] + [[package]] name = "rtnetlink" version = "0.20.0" @@ -10675,6 +11087,19 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_codegen_spirv-types" +version = "0.10.0-alpha.1" +source = "git+https://github.com/Rust-GPU/rust-gpu?rev=bd49568baf08301107c9f698494735724980dee0#bd49568baf08301107c9f698494735724980dee0" +dependencies = [ + "rspirv", + "semver 1.0.27", + "serde", + "serde_json", + "spirv", + "thiserror 2.0.18", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -11773,7 +12198,7 @@ version = "0.56.0" source = "git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d69071449e988d6e63e4122c#8304f8852018c9b2d69071449e988d6e63e4122c" dependencies = [ "async-trait", - "directories", + "directories 5.0.1", "exit-future", "futures", "futures-timer", @@ -12408,6 +12833,28 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float 2.10.1", + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.19" @@ -13938,6 +14385,61 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spirv" +version = "0.4.0+sdk-1.4.341.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" +dependencies = [ + "bitflags 2.11.0", + "serde", +] + +[[package]] +name = "spirv-builder" +version = "0.10.0-alpha.1" +source = "git+https://github.com/Rust-GPU/rust-gpu?rev=bd49568baf08301107c9f698494735724980dee0#bd49568baf08301107c9f698494735724980dee0" +dependencies = [ + "cargo_metadata 0.21.0", + "log", + "memchr", + "raw-string", + "rustc_codegen_spirv-types", + "semver 1.0.27", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "spirv-std" +version = "0.10.0-alpha.1" +source = "git+https://github.com/Rust-GPU/rust-gpu?rev=bd49568baf08301107c9f698494735724980dee0#bd49568baf08301107c9f698494735724980dee0" +dependencies = [ + "bitflags 1.3.2", + "glam", + "libm", + "num-traits", + "spirv-std-macros", + "spirv-std-types", +] + +[[package]] +name = "spirv-std-macros" +version = "0.10.0-alpha.1" +source = "git+https://github.com/Rust-GPU/rust-gpu?rev=bd49568baf08301107c9f698494735724980dee0#bd49568baf08301107c9f698494735724980dee0" +dependencies = [ + "proc-macro2", + "quote", + "spirv-std-types", + "syn 2.0.118", +] + +[[package]] +name = "spirv-std-types" +version = "0.10.0-alpha.1" +source = "git+https://github.com/Rust-GPU/rust-gpu?rev=bd49568baf08301107c9f698494735724980dee0#bd49568baf08301107c9f698494735724980dee0" + [[package]] name = "spki" version = "0.7.3" @@ -14307,6 +14809,7 @@ dependencies = [ "subspace-process", "subspace-proof-of-space", "subspace-proof-of-space-gpu", + "subspace-proof-of-space-wgpu", "subspace-rpc-primitives", "subspace-verification", "substrate-bip39", @@ -14628,6 +15131,7 @@ name = "subspace-proof-of-space" version = "0.1.0" dependencies = [ "ab-chacha8 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ab-core-primitives", "ab-proof-of-space", "blake3", "chacha20 0.10.1", @@ -14655,6 +15159,16 @@ dependencies = [ "subspace-proof-of-space", ] +[[package]] +name = "subspace-proof-of-space-wgpu" +version = "0.1.0" +dependencies = [ + "ab-proof-of-space-gpu", + "subspace-core-primitives", + "subspace-erasure-coding", + "subspace-kzg", +] + [[package]] name = "subspace-proof-of-time" version = "0.1.0" @@ -15117,7 +15631,7 @@ version = "31.1.0" source = "git+https://github.com/autonomys/polkadot-sdk?rev=8304f8852018c9b2d69071449e988d6e63e4122c#8304f8852018c9b2d69071449e988d6e63e4122c" dependencies = [ "build-helper", - "cargo_metadata", + "cargo_metadata 0.15.4", "console", "filetime", "jobserver", @@ -16140,6 +16654,12 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.19.0" @@ -17008,6 +17528,151 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "wgpu" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" +dependencies = [ + "arrayvec 0.7.6", + "bitflags 2.11.0", + "bytemuck", + "cfg-if", + "cfg_aliases 0.2.1", + "document-features", + "hashbrown 0.16.1", + "js-sys", + "log", + "naga", + "parking_lot 0.12.5", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f519832254e56965a9940c4af57dcb75f702b6f6fa4a0b172f685395843a4d7" +dependencies = [ + "arrayvec 0.7.6", + "bit-set 0.9.1", + "bit-vec 0.9.1", + "bitflags 2.11.0", + "bytemuck", + "cfg_aliases 0.2.1", + "document-features", + "hashbrown 0.16.1", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot 0.12.5", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.18", + "wgpu-core-deps-apple", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-naga-bridge", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-apple" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e39e26c4c0e07589e67d18546cf79ff45383659fc72fca4dd293358a0347f3" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e592c1bbef6ad047647ae6e666ebd8cee7a32bb4544d9700ec96cbf73230257" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ace1c17727311c22a46e4e3faf56ea6de81af99dcc839bdfb54857b94d448d" +dependencies = [ + "android_system_properties", + "arrayvec 0.7.6", + "ash", + "bitflags 2.11.0", + "block2", + "bytemuck", + "cfg-if", + "cfg_aliases 0.2.1", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "libc", + "libloading", + "log", + "naga", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", + "objc2-quartz-core", + "ordered-float 5.3.0", + "parking_lot 0.12.5", + "portable-atomic", + "portable-atomic-util", + "profiling", + "raw-window-handle", + "raw-window-metal", + "renderdoc-sys", + "smallvec", + "thiserror 2.0.18", + "wgpu-naga-bridge", + "wgpu-types", + "windows 0.62.2", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95226013f547544b223281cd16a4fb549aa9dcb562adbda0faae4c73ffbbc161" +dependencies = [ + "naga", + "wgpu-types", +] + +[[package]] +name = "wgpu-types" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" +dependencies = [ + "bitflags 2.11.0", + "bytemuck", + "js-sys", + "log", + "raw-window-handle", + "web-sys", +] + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index 16b56e394e3..4010f2e8f9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,9 @@ members = [ [workspace.dependencies] ab-chacha8 = "0.1.0" +ab-core-primitives = { git = "https://github.com/nazar-pc/abundance", rev = "fc8da9992575a3b33ea9c5c764ea71f926f4592e", default-features = false } ab-proof-of-space = { git = "https://github.com/nazar-pc/abundance", rev = "fc8da9992575a3b33ea9c5c764ea71f926f4592e", default-features = false } +ab-proof-of-space-gpu = { git = "https://github.com/nazar-pc/abundance", rev = "fc8da9992575a3b33ea9c5c764ea71f926f4592e", default-features = false } actix-web = { version = "4.9.0", default-features = false } aes = "0.9.0" anyhow = "1.0.89" @@ -277,6 +279,7 @@ subspace-networking = { version = "0.1.0", path = "crates/subspace-networking" } subspace-process = { version = "0.0.1", path = "shared/subspace-process" } subspace-proof-of-space = { version = "0.1.0", path = "crates/subspace-proof-of-space", default-features = false } subspace-proof-of-space-gpu = { version = "0.1.0", path = "shared/subspace-proof-of-space-gpu" } +subspace-proof-of-space-wgpu = { version = "0.1.0", path = "shared/subspace-proof-of-space-wgpu" } subspace-proof-of-time = { version = "0.1.0", path = "crates/subspace-proof-of-time" } subspace-rpc-primitives = { version = "0.1.0", path = "crates/subspace-rpc-primitives" } subspace-runtime = { version = "0.1.0", path = "crates/subspace-runtime" } @@ -446,3 +449,10 @@ substrate-bip39 = "=0.6.0" # Patch away `libp2p-identity` in our dependency tree with the git version. # For details see: https://github.com/autonomys/rust-libp2p/blob/676c687c2f7a6e92f8f4f77a6934022aec3ba16c/Cargo.toml#L140-L145 libp2p-identity = { git = "https://github.com/autonomys/rust-libp2p", rev = "676c687c2f7a6e92f8f4f77a6934022aec3ba16c" } + +# Point the abundance dependencies at my fork for review; revert to an upstream nazar-pc rev once +# these abundance changes are merged. +[patch."https://github.com/nazar-pc/abundance"] +ab-core-primitives = { git = "https://github.com/vedhavyas/abundance", rev = "834512648755f40a2aa0dc7d4fc44c66e2c3fa47" } +ab-proof-of-space = { git = "https://github.com/vedhavyas/abundance", rev = "834512648755f40a2aa0dc7d4fc44c66e2c3fa47" } +ab-proof-of-space-gpu = { git = "https://github.com/vedhavyas/abundance", rev = "834512648755f40a2aa0dc7d4fc44c66e2c3fa47" } diff --git a/crates/subspace-farmer/Cargo.toml b/crates/subspace-farmer/Cargo.toml index 3d7d93f02f0..8a41253418b 100644 --- a/crates/subspace-farmer/Cargo.toml +++ b/crates/subspace-farmer/Cargo.toml @@ -59,6 +59,7 @@ subspace-metrics = { workspace = true, optional = true } subspace-networking.workspace = true subspace-process.workspace = true subspace-proof-of-space.workspace = true +subspace-proof-of-space-wgpu = { workspace = true, optional = true } subspace-rpc-primitives.workspace = true subspace-verification = { workspace = true, features = ["kzg"] } substrate-bip39.workspace = true @@ -84,6 +85,8 @@ cuda = ["_gpu", "subspace-proof-of-space-gpu/cuda"] # TODO: ROCm can't be enabled at the same time as `cuda` feature at the moment # Seems to support RDNA 2+, at least on Linux rocm = ["_gpu", "subspace-proof-of-space-gpu/rocm"] +# Vulkan/Metal plotting via wgpu; unlike cuda/rocm it is available on macOS too +wgpu = ["_gpu", "dep:subspace-proof-of-space-wgpu"] # Internal feature, shouldn't be used directly _gpu = [] diff --git a/crates/subspace-farmer/src/bin/subspace-farmer/commands/cluster/plotter.rs b/crates/subspace-farmer/src/bin/subspace-farmer/commands/cluster/plotter.rs index 3b44000001f..50df51a1be7 100644 --- a/crates/subspace-farmer/src/bin/subspace-farmer/commands/cluster/plotter.rs +++ b/crates/subspace-farmer/src/bin/subspace-farmer/commands/cluster/plotter.rs @@ -22,6 +22,8 @@ use subspace_farmer::plotter::gpu::GpuPlotter; use subspace_farmer::plotter::gpu::cuda::CudaRecordsEncoder; #[cfg(feature = "rocm")] use subspace_farmer::plotter::gpu::rocm::RocmRecordsEncoder; +#[cfg(feature = "wgpu")] +use subspace_farmer::plotter::gpu::wgpu::WgpuRecordsEncoder; use subspace_farmer::plotter::pool::PoolPlotter; use subspace_farmer::utils::{ create_plotting_thread_pool_manager, parse_cpu_cores_sets, thread_pool_core_indices, @@ -119,6 +121,24 @@ struct RocmPlottingOptions { rocm_gpus: Option, } +#[cfg(feature = "wgpu")] +#[derive(Debug, Parser)] +struct WgpuPlottingOptions { + /// How many sectors farmer will download concurrently during plotting with wgpu GPUs. + /// Limits memory usage of the plotting process. Defaults to the number of wgpu GPUs * 3, + /// to download future sectors ahead of time. + /// + /// Increasing this value will cause higher memory usage. + #[arg(long)] + wgpu_sector_downloading_concurrency: Option, + /// Set the exact GPUs to be used for plotting, instead of using all GPUs (default behavior). + /// + /// GPUs are coma-separated: `--wgpu-gpus 0,1,3`. Use an empty string to disable wgpu + /// GPUs. + #[arg(long)] + wgpu_gpus: Option, +} + /// Arguments for plotter #[derive(Debug, Parser)] pub(super) struct PlotterArgs { @@ -133,6 +153,10 @@ pub(super) struct PlotterArgs { #[cfg(feature = "rocm")] #[clap(flatten)] rocm_plotting_options: RocmPlottingOptions, + /// Plotting options only used by wgpu GPU plotter + #[cfg(feature = "wgpu")] + #[clap(flatten)] + wgpu_plotting_options: WgpuPlottingOptions, /// Cache group to use if specified, otherwise all caches are usable by this plotter #[arg(long)] cache_group: Option, @@ -155,6 +179,8 @@ where cuda_plotting_options, #[cfg(feature = "rocm")] rocm_plotting_options, + #[cfg(feature = "wgpu")] + wgpu_plotting_options, cache_group, additional_components: _, } = plotter_args; @@ -201,6 +227,22 @@ where plotters.push(Box::new(rocm_plotter)); } } + #[cfg(feature = "wgpu")] + { + let maybe_wgpu_plotter = init_wgpu_plotter( + wgpu_plotting_options, + piece_getter.clone(), + Arc::clone(&global_mutex), + kzg.clone(), + erasure_coding.clone(), + registry, + ) + .await?; + + if let Some(wgpu_plotter) = maybe_wgpu_plotter { + plotters.push(Box::new(wgpu_plotter)); + } + } { let cpu_sector_encoding_concurrency = cpu_plotting_options.cpu_sector_encoding_concurrency; let maybe_cpu_plotter = init_cpu_plotter::<_, PosTable>( @@ -495,3 +537,96 @@ where .map_err(|error| anyhow::anyhow!("Failed to initialize ROCm plotter: {error}"))?, )) } + +#[cfg(feature = "wgpu")] +async fn init_wgpu_plotter( + wgpu_plotting_options: WgpuPlottingOptions, + piece_getter: PG, + global_mutex: Arc>, + kzg: Kzg, + erasure_coding: ErasureCoding, + registry: &mut Registry, +) -> anyhow::Result>> +where + PG: PieceGetter + Clone + Send + Sync + 'static, +{ + use std::collections::BTreeSet; + use std::num::NonZeroU8; + use subspace_proof_of_space_wgpu::{Device, DeviceType, WgpuDevice}; + use tracing::{debug, warn}; + + let WgpuPlottingOptions { + wgpu_sector_downloading_concurrency, + wgpu_gpus, + } = wgpu_plotting_options; + + // A couple of queues per device so downloads and encoding overlap on the GPU + let number_of_queues = |_device_type: DeviceType| NonZeroU8::new(2).expect("Not zero; qed"); + let mut wgpu_devices = Device::enumerate(number_of_queues).await; + let mut used_wgpu_devices = (0..wgpu_devices.len()).collect::>(); + + if let Some(wgpu_gpus) = wgpu_gpus { + if wgpu_gpus.is_empty() { + info!("wgpu GPU plotting was explicitly disabled"); + return Ok(None); + } + + let mut wgpu_gpus_to_use = wgpu_gpus + .split(',') + .map(|gpu_index| gpu_index.parse()) + .collect::, _>>()?; + + (used_wgpu_devices, wgpu_devices) = wgpu_devices + .into_iter() + .enumerate() + .filter(|(index, _wgpu_device)| wgpu_gpus_to_use.remove(index)) + .unzip(); + + if !wgpu_gpus_to_use.is_empty() { + warn!( + ?wgpu_gpus_to_use, + "Some wgpu GPUs were not found on the system" + ); + } + } + + if wgpu_devices.is_empty() { + debug!("No wgpu GPU devices found"); + return Ok(None); + } + + info!(?used_wgpu_devices, "Using wgpu GPUs"); + + let wgpu_downloading_semaphore = Arc::new(Semaphore::new( + wgpu_sector_downloading_concurrency + .map(|wgpu_sector_downloading_concurrency| wgpu_sector_downloading_concurrency.get()) + .unwrap_or(wgpu_devices.len() * 3), + )); + + Ok(Some( + GpuPlotter::new( + piece_getter, + wgpu_downloading_semaphore, + wgpu_devices + .into_iter() + .map(|wgpu_device| { + let id = wgpu_device.id(); + let queue_devices = wgpu_device + .create_proofs_encoder_instances(true) + .into_iter() + .map(|instance| WgpuDevice::new(instance, erasure_coding.clone())) + .collect(); + WgpuRecordsEncoder::new(id, queue_devices, Arc::clone(&global_mutex)) + }) + .collect::>() + .map_err(|error| { + anyhow::anyhow!("Failed to create wgpu records encoder: {error}") + })?, + global_mutex, + kzg, + erasure_coding, + Some(registry), + ) + .map_err(|error| anyhow::anyhow!("Failed to initialize wgpu plotter: {error}"))?, + )) +} diff --git a/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm.rs b/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm.rs index e06bcab2624..24c8a3c9d14 100644 --- a/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm.rs +++ b/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm.rs @@ -37,6 +37,8 @@ use subspace_farmer::plotter::gpu::GpuPlotter; use subspace_farmer::plotter::gpu::cuda::CudaRecordsEncoder; #[cfg(feature = "rocm")] use subspace_farmer::plotter::gpu::rocm::RocmRecordsEncoder; +#[cfg(feature = "wgpu")] +use subspace_farmer::plotter::gpu::wgpu::WgpuRecordsEncoder; use subspace_farmer::plotter::pool::PoolPlotter; use subspace_farmer::single_disk_farm::identity::Identity; use subspace_farmer::single_disk_farm::{ @@ -180,6 +182,24 @@ struct RocmPlottingOptions { rocm_gpus: Option, } +#[cfg(feature = "wgpu")] +#[derive(Debug, Parser)] +struct WgpuPlottingOptions { + /// How many sectors farmer will download concurrently during plotting with wgpu GPUs. + /// Limits memory usage of the plotting process. Defaults to the number of wgpu GPUs * 3, + /// to download future sectors ahead of time. + /// + /// Increasing this value will cause higher memory usage. + #[arg(long)] + wgpu_sector_downloading_concurrency: Option, + /// Set the exact GPUs to be used for plotting instead of using all GPUs (default behavior). + /// + /// GPUs are coma-separated: `--wgpu-gpus 0,1,3`. Use an empty string to disable wgpu + /// GPUs. + #[arg(long)] + wgpu_gpus: Option, +} + /// Arguments for farmer #[derive(Debug, Parser)] pub(crate) struct FarmingArgs { @@ -252,6 +272,10 @@ pub(crate) struct FarmingArgs { #[cfg(feature = "rocm")] #[clap(flatten)] rocm_plotting_options: RocmPlottingOptions, + /// Plotting options only used by wgpu GPU plotter + #[cfg(feature = "wgpu")] + #[clap(flatten)] + wgpu_plotting_options: WgpuPlottingOptions, /// How many sectors a will be plotted concurrently per farm. /// /// Defaults to 2, but can be decreased if there is a large number of farms available to @@ -321,6 +345,8 @@ where cuda_plotting_options, #[cfg(feature = "rocm")] rocm_plotting_options, + #[cfg(feature = "wgpu")] + wgpu_plotting_options, max_plotting_sectors_per_farm, plot_cache, disable_farm_locking, @@ -551,6 +577,22 @@ where plotters.push(Box::new(rocm_plotter)); } } + #[cfg(feature = "wgpu")] + { + let maybe_wgpu_plotter = init_wgpu_plotter( + wgpu_plotting_options, + piece_getter.clone(), + Arc::clone(&global_mutex), + kzg.clone(), + erasure_coding.clone(), + &mut registry, + ) + .await?; + + if let Some(wgpu_plotter) = maybe_wgpu_plotter { + plotters.push(Box::new(wgpu_plotter)); + } + } { let cpu_sector_encoding_concurrency = cpu_plotting_options.cpu_sector_encoding_concurrency; let maybe_cpu_plotter = init_cpu_plotter::<_, PosTable>( @@ -1084,6 +1126,99 @@ where )) } +#[cfg(feature = "wgpu")] +async fn init_wgpu_plotter( + wgpu_plotting_options: WgpuPlottingOptions, + piece_getter: PG, + global_mutex: Arc>, + kzg: Kzg, + erasure_coding: ErasureCoding, + registry: &mut Registry, +) -> anyhow::Result>> +where + PG: PieceGetter + Clone + Send + Sync + 'static, +{ + use std::collections::BTreeSet; + use std::num::NonZeroU8; + use subspace_proof_of_space_wgpu::{Device, DeviceType, WgpuDevice}; + use tracing::debug; + + let WgpuPlottingOptions { + wgpu_sector_downloading_concurrency, + wgpu_gpus, + } = wgpu_plotting_options; + + // A couple of queues per device so downloads and encoding overlap on the GPU + let number_of_queues = |_device_type: DeviceType| NonZeroU8::new(2).expect("Not zero; qed"); + let mut wgpu_devices = Device::enumerate(number_of_queues).await; + let mut used_wgpu_devices = (0..wgpu_devices.len()).collect::>(); + + if let Some(wgpu_gpus) = wgpu_gpus { + if wgpu_gpus.is_empty() { + info!("wgpu GPU plotting was explicitly disabled"); + return Ok(None); + } + + let mut wgpu_gpus_to_use = wgpu_gpus + .split(',') + .map(|gpu_index| gpu_index.parse()) + .collect::, _>>()?; + + (used_wgpu_devices, wgpu_devices) = wgpu_devices + .into_iter() + .enumerate() + .filter(|(index, _wgpu_device)| wgpu_gpus_to_use.remove(index)) + .unzip(); + + if !wgpu_gpus_to_use.is_empty() { + warn!( + ?wgpu_gpus_to_use, + "Some wgpu GPUs were not found on the system" + ); + } + } + + if wgpu_devices.is_empty() { + debug!("No wgpu GPU devices found"); + return Ok(None); + } + + info!(?used_wgpu_devices, "Using wgpu GPUs"); + + let wgpu_downloading_semaphore = Arc::new(Semaphore::new( + wgpu_sector_downloading_concurrency + .map(|wgpu_sector_downloading_concurrency| wgpu_sector_downloading_concurrency.get()) + .unwrap_or(wgpu_devices.len() * 3), + )); + + Ok(Some( + GpuPlotter::new( + piece_getter, + wgpu_downloading_semaphore, + wgpu_devices + .into_iter() + .map(|wgpu_device| { + let id = wgpu_device.id(); + let queue_devices = wgpu_device + .create_proofs_encoder_instances(true) + .into_iter() + .map(|instance| WgpuDevice::new(instance, erasure_coding.clone())) + .collect(); + WgpuRecordsEncoder::new(id, queue_devices, Arc::clone(&global_mutex)) + }) + .collect::>() + .map_err(|error| { + anyhow::anyhow!("Failed to create wgpu records encoder: {error}") + })?, + global_mutex, + kzg, + erasure_coding, + Some(registry), + ) + .map_err(|error| anyhow::anyhow!("Failed to initialize wgpu plotter: {error}"))?, + )) +} + #[cfg(feature = "rocm")] fn init_rocm_plotter( rocm_plotting_options: RocmPlottingOptions, diff --git a/crates/subspace-farmer/src/plotter/gpu.rs b/crates/subspace-farmer/src/plotter/gpu.rs index 833486700ff..0fd64d5f4b2 100644 --- a/crates/subspace-farmer/src/plotter/gpu.rs +++ b/crates/subspace-farmer/src/plotter/gpu.rs @@ -6,6 +6,8 @@ mod gpu_encoders_manager; pub mod metrics; #[cfg(feature = "rocm")] pub mod rocm; +#[cfg(feature = "wgpu")] +pub mod wgpu; use crate::plotter::gpu::gpu_encoders_manager::GpuRecordsEncoderManager; use crate::plotter::gpu::metrics::GpuPlotterMetrics; diff --git a/crates/subspace-farmer/src/plotter/gpu/wgpu.rs b/crates/subspace-farmer/src/plotter/gpu/wgpu.rs new file mode 100644 index 00000000000..2f33708486b --- /dev/null +++ b/crates/subspace-farmer/src/plotter/gpu/wgpu.rs @@ -0,0 +1,140 @@ +//! wgpu GPU records encoder + +use crate::plotter::gpu::GpuRecordsEncoder; +use async_lock::Mutex as AsyncMutex; +use parking_lot::Mutex; +use rayon::{ThreadPool, ThreadPoolBuildError, ThreadPoolBuilder, current_thread_index}; +use std::fmt; +use std::process::exit; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use subspace_core_primitives::pieces::{PieceOffset, Record}; +use subspace_core_primitives::sectors::SectorId; +use subspace_farmer_components::plotting::RecordsEncoder; +use subspace_farmer_components::sector::SectorContentsMap; +use subspace_proof_of_space_wgpu::WgpuDevice; + +/// wgpu implementation of [`GpuRecordsEncoder`] +pub struct WgpuRecordsEncoder { + devices: Vec>, + thread_pool: ThreadPool, + global_mutex: Arc>, +} + +impl fmt::Debug for WgpuRecordsEncoder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WgpuRecordsEncoder") + .finish_non_exhaustive() + } +} + +impl GpuRecordsEncoder for WgpuRecordsEncoder { + const TYPE: &'static str = "wgpu"; +} + +impl RecordsEncoder for WgpuRecordsEncoder { + fn encode_records( + &mut self, + sector_id: &SectorId, + records: &mut [Record], + abort_early: &AtomicBool, + ) -> anyhow::Result { + let pieces_in_sector = records + .len() + .try_into() + .map_err(|error| anyhow::anyhow!("Failed to convert pieces in sector: {error}"))?; + let mut sector_contents_map = SectorContentsMap::new(pieces_in_sector); + + { + let iter = Mutex::new( + (PieceOffset::ZERO..) + .zip(records.iter_mut()) + .zip(sector_contents_map.iter_record_bitfields_mut()), + ); + let plotting_error = Mutex::new(None::); + + self.thread_pool.scope(|scope| { + scope.spawn_broadcast(|_scope, _ctx| { + // One device (GPU queue) per pool thread, so this lock is always uncontended + let thread_index = current_thread_index().unwrap_or_default(); + let Some(device) = self.devices.get(thread_index) else { + return; + }; + let mut device = device + .try_lock() + .expect("1:1 mapping between threads and devices; qed"); + + loop { + // Take mutex briefly to make sure encoding is allowed right now + self.global_mutex.lock_blocking(); + + // This instead of `while` above because otherwise mutex will be held for the + // duration of the loop and will limit concurrency to 1 record + let Some(((piece_offset, record), mut encoded_chunks_used)) = + iter.lock().next() + else { + return; + }; + let pos_seed = sector_id.derive_evaluation_seed(piece_offset); + + if let Err(error) = device.generate_and_encode_pospace( + &pos_seed, + record, + encoded_chunks_used.iter_mut(), + ) { + plotting_error.lock().replace(error); + return; + } + + if abort_early.load(Ordering::Relaxed) { + return; + } + } + }); + }); + + let plotting_error = plotting_error.lock().take(); + if let Some(error) = plotting_error { + return Err(anyhow::Error::msg(error)); + } + } + + Ok(sector_contents_map) + } +} + +impl WgpuRecordsEncoder { + /// Create new instance. + /// + /// One thread is spawned per device (GPU queue), so records encode concurrently across queues. + pub fn new( + id: u32, + devices: Vec, + global_mutex: Arc>, + ) -> Result { + let thread_name = move |thread_index| format!("wgpu-{id:02}.{thread_index:02}"); + // TODO: remove this panic handler when rayon logs panic_info + // https://github.com/rayon-rs/rayon/issues/1208 + let panic_handler = move |panic_info| { + if let Some(index) = current_thread_index() { + eprintln!("panic on thread {}: {:?}", thread_name(index), panic_info); + } else { + // We want to guarantee exit, rather than panicking in a panic handler. + eprintln!("rayon panic handler called on non-rayon thread: {panic_info:?}"); + } + exit(1); + }; + + let thread_pool = ThreadPoolBuilder::new() + .thread_name(thread_name) + .panic_handler(panic_handler) + .num_threads(devices.len()) + .build()?; + + Ok(Self { + devices: devices.into_iter().map(Mutex::new).collect(), + thread_pool, + global_mutex, + }) + } +} diff --git a/crates/subspace-proof-of-space/Cargo.toml b/crates/subspace-proof-of-space/Cargo.toml index a903e083133..ca2dccf3496 100644 --- a/crates/subspace-proof-of-space/Cargo.toml +++ b/crates/subspace-proof-of-space/Cargo.toml @@ -19,6 +19,7 @@ bench = false [dependencies] ab-chacha8.workspace = true +ab-core-primitives = { workspace = true, optional = true } ab-proof-of-space = { workspace = true, optional = true } blake3 = { workspace = true, default-features = false } chacha20 = { workspace = true, features = ["cipher"], optional = true } @@ -45,6 +46,7 @@ default = [] alloc = [ "dep:chacha20", "dep:seq-macro", + "dep:ab-core-primitives", "dep:ab-proof-of-space", "ab-proof-of-space/alloc", "ab-proof-of-space/full-chiapos", diff --git a/crates/subspace-proof-of-space/src/chia_v2.rs b/crates/subspace-proof-of-space/src/chia_v2.rs index 959bde781af..41c859856d7 100644 --- a/crates/subspace-proof-of-space/src/chia_v2.rs +++ b/crates/subspace-proof-of-space/src/chia_v2.rs @@ -5,7 +5,10 @@ use crate::chia::ChiaTable; use crate::{PosTableType, Table, TableGenerator}; -use ab_proof_of_space::chiapos::{Tables, TablesCache}; +use ab_core_primitives::sectors::SBucket; +use ab_proof_of_space::chiapos::{Proofs, Tables, TablesCache}; +use alloc::boxed::Box; +use core::fmt; use subspace_core_primitives::pos::{PosProof, PosSeed}; use subspace_core_primitives::solutions::SolutionPotVerifier; @@ -22,14 +25,14 @@ pub struct ChiaV2TableGenerator { impl TableGenerator for ChiaV2TableGenerator { fn generate(&self, seed: &PosSeed) -> ChiaV2Table { ChiaV2Table { - tables: Tables::::create((*seed).into(), &self.tables_cache), + proofs: Tables::::create_proofs::((*seed).into(), &self.tables_cache), } } #[cfg(feature = "parallel")] fn generate_parallel(&self, seed: &PosSeed) -> ChiaV2Table { ChiaV2Table { - tables: Tables::::create_parallel((*seed).into(), &self.tables_cache), + proofs: Tables::::create_proofs_parallel::((*seed).into(), &self.tables_cache), } } } @@ -37,9 +40,14 @@ impl TableGenerator for ChiaV2TableGenerator { /// Proof of space table. /// /// Chia implementation. -#[derive(Debug)] pub struct ChiaV2Table { - tables: Tables, + proofs: Box>, +} + +impl fmt::Debug for ChiaV2Table { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ChiaV2Table").finish_non_exhaustive() + } } impl SolutionPotVerifier for ChiaV2Table { @@ -53,9 +61,8 @@ impl Table for ChiaV2Table { type Generator = ChiaV2TableGenerator; fn find_proof(&self, challenge_index: u32) -> Option { - self.tables - .find_proof(challenge_index.to_le_bytes()) - .next() + self.proofs + .for_s_bucket(SBucket::from(challenge_index as u16)) .map(PosProof::from) } diff --git a/shared/subspace-proof-of-space-wgpu/Cargo.toml b/shared/subspace-proof-of-space-wgpu/Cargo.toml new file mode 100644 index 00000000000..e8e9459f23f --- /dev/null +++ b/shared/subspace-proof-of-space-wgpu/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "subspace-proof-of-space-wgpu" +description = "Subspace proof of space plotting for GPU via wgpu (Vulkan/Metal)" +license = "0BSD" +version = "0.1.0" +edition.workspace = true +include = [ + "/src", + "/Cargo.toml", +] + +[dependencies] +ab-proof-of-space-gpu = { workspace = true } +subspace-core-primitives = { workspace = true } +subspace-erasure-coding = { workspace = true } +subspace-kzg = { workspace = true } + +[lints] +workspace = true diff --git a/shared/subspace-proof-of-space-wgpu/src/lib.rs b/shared/subspace-proof-of-space-wgpu/src/lib.rs new file mode 100644 index 00000000000..e555055303b --- /dev/null +++ b/shared/subspace-proof-of-space-wgpu/src/lib.rs @@ -0,0 +1,133 @@ +//! Subspace proof of space plotting for GPU via wgpu (Vulkan/Metal). +//! +//! The GPU (abundance's `ab-proof-of-space-gpu`) produces Chia proofs; records are then encoded +//! with subspace's KZG scheme so GPU-plotted sectors read back under the existing runtime, byte +//! for byte the same as the CPU encoder. + +#![feature(portable_simd)] + +use ab_proof_of_space_gpu::GpuRecordsEncoderInstance; +use std::ops::DerefMut; +use std::simd::Simd; +use subspace_core_primitives::ScalarBytes; +use subspace_core_primitives::pieces::Record; +use subspace_core_primitives::pos::PosSeed; +use subspace_erasure_coding::ErasureCoding; +use subspace_kzg::Scalar; + +pub use ab_proof_of_space_gpu::{Backend, Device, DeviceType}; + +/// A single wgpu proof-of-space encoder (one GPU queue) plus the erasure coding used to encode +/// records host-side. +pub struct WgpuDevice { + instance: GpuRecordsEncoderInstance, + erasure_coding: ErasureCoding, +} + +impl WgpuDevice { + /// Create a new device from an abundance proof encoder instance. + pub fn new(instance: GpuRecordsEncoderInstance, erasure_coding: ErasureCoding) -> Self { + Self { + instance, + erasure_coding, + } + } + + /// Generate proofs on the GPU and encode a record with subspace's KZG scheme. + /// + /// Mirrors `record_encoding` in `subspace-farmer-components`, so output is byte-identical to + /// the CPU encoder. + pub fn generate_and_encode_pospace( + &mut self, + seed: &PosSeed, + record: &mut Record, + encoded_chunks_used_output: impl ExactSizeIterator>, + ) -> Result<(), String> { + let proofs = self + .instance + .create_proofs(&(**seed).into()) + .map_err(|error| error.to_string())?; + let proofs = proofs.proofs(); + + let source_record_chunks = record.to_vec(); + let parity_record_chunks = self + .erasure_coding + .extend( + &source_record_chunks + .iter() + .map(|scalar_bytes| { + Scalar::try_from(scalar_bytes) + .expect("Record chunks are valid scalar bytes; qed") + }) + .collect::>(), + ) + .expect("Erasure coding instance supports this many shards; qed") + .into_iter() + .map(<[u8; ScalarBytes::FULL_BYTES]>::from) + .collect::>(); + + let mut encoded_chunks_used = vec![false; Record::NUM_S_BUCKETS]; + let mut chunks_scratch = + Vec::<[u8; ScalarBytes::FULL_BYTES]>::with_capacity(Record::NUM_S_BUCKETS); + for s_bucket in 0..Record::NUM_S_BUCKETS { + let record_chunk = if s_bucket % 2 == 0 { + &source_record_chunks[s_bucket / 2] + } else { + &parity_record_chunks[s_bucket / 2] + }; + + let proof_found = (proofs.found_proofs[s_bucket / u8::BITS as usize] + >> (s_bucket % u8::BITS as usize)) + & 1 + == 1; + let encoded_chunk = if proof_found { + (Simd::from(*record_chunk) ^ Simd::from(*proofs.proofs[s_bucket].hash())).to_array() + } else { + // Dummy value indicating no proof + [0; ScalarBytes::FULL_BYTES] + }; + chunks_scratch.push(encoded_chunk); + } + + let num_successfully_encoded_chunks = chunks_scratch + .drain(..) + .zip(encoded_chunks_used.iter_mut()) + .filter_map(|(maybe_encoded_chunk, encoded_chunk_used)| { + if maybe_encoded_chunk == [0; ScalarBytes::FULL_BYTES] { + None + } else { + *encoded_chunk_used = true; + Some(maybe_encoded_chunk) + } + }) + .take(record.len()) + .zip(record.iter_mut()) + .map(|(input_chunk, output_chunk)| { + *output_chunk = input_chunk; + }) + .count(); + + source_record_chunks + .iter() + .zip(&parity_record_chunks) + .flat_map(|(a, b)| [a, b]) + .zip(encoded_chunks_used.iter()) + .filter_map(|(record_chunk, encoded_chunk_used)| { + if *encoded_chunk_used { + None + } else { + Some(record_chunk) + } + }) + .zip(record.iter_mut().skip(num_successfully_encoded_chunks)) + .for_each(|(input_chunk, output_chunk)| { + *output_chunk = *input_chunk; + }); + + encoded_chunks_used_output + .zip(&encoded_chunks_used) + .for_each(|(mut output, input)| *output = *input); + + Ok(()) + } +} From 19a24685a9b844eb1171298965d4507281b2077e Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi <7549475+vedhavyas@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:02:04 +0530 Subject: [PATCH 7/8] point abundance patch at find_proofs Metal fix --- Cargo.lock | 21 +++++++++++---------- Cargo.toml | 6 +++--- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e6cdb51c9e..5ef6243eba7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,7 @@ dependencies = [ [[package]] name = "ab-blake3" version = "0.1.0" -source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +source = "git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e#6d5c97150b639a8fae80683b180b84c11453545e" dependencies = [ "blake3", ] @@ -29,12 +29,12 @@ checksum = "78928d0e8969c1b539a98de5cf3a678d136f160415c765f3f4c464b5eee39976" [[package]] name = "ab-chacha8" version = "0.1.0" -source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +source = "git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e#6d5c97150b639a8fae80683b180b84c11453545e" [[package]] name = "ab-core-primitives" version = "0.1.0" -source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +source = "git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e#6d5c97150b639a8fae80683b180b84c11453545e" dependencies = [ "ab-blake3", "ab-io-type", @@ -54,7 +54,7 @@ dependencies = [ [[package]] name = "ab-io-type" version = "0.1.0" -source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +source = "git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e#6d5c97150b639a8fae80683b180b84c11453545e" dependencies = [ "ab-io-type-derive", ] @@ -62,7 +62,7 @@ dependencies = [ [[package]] name = "ab-io-type-derive" version = "0.1.0" -source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +source = "git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e#6d5c97150b639a8fae80683b180b84c11453545e" dependencies = [ "proc-macro2", "quote", @@ -72,7 +72,7 @@ dependencies = [ [[package]] name = "ab-merkle-tree" version = "0.1.0" -source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +source = "git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e#6d5c97150b639a8fae80683b180b84c11453545e" dependencies = [ "ab-blake3", ] @@ -80,10 +80,10 @@ dependencies = [ [[package]] name = "ab-proof-of-space" version = "0.1.0" -source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +source = "git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e#6d5c97150b639a8fae80683b180b84c11453545e" dependencies = [ "ab-blake3", - "ab-chacha8 0.1.0 (git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47)", + "ab-chacha8 0.1.0 (git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e)", "ab-core-primitives", "chacha20 0.10.1", "derive_more 2.1.1", @@ -96,10 +96,10 @@ dependencies = [ [[package]] name = "ab-proof-of-space-gpu" version = "0.1.0" -source = "git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47#834512648755f40a2aa0dc7d4fc44c66e2c3fa47" +source = "git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e#6d5c97150b639a8fae80683b180b84c11453545e" dependencies = [ "ab-blake3", - "ab-chacha8 0.1.0 (git+https://github.com/vedhavyas/abundance?rev=834512648755f40a2aa0dc7d4fc44c66e2c3fa47)", + "ab-chacha8 0.1.0 (git+https://github.com/vedhavyas/abundance?rev=6d5c97150b639a8fae80683b180b84c11453545e)", "ab-core-primitives", "ab-proof-of-space", "anyhow", @@ -14849,6 +14849,7 @@ dependencies = [ "subspace-erasure-coding", "subspace-kzg", "subspace-proof-of-space", + "subspace-proof-of-space-wgpu", "subspace-verification", "thiserror 2.0.18", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 4010f2e8f9f..07577f4fa53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -453,6 +453,6 @@ libp2p-identity = { git = "https://github.com/autonomys/rust-libp2p", rev = "676 # Point the abundance dependencies at my fork for review; revert to an upstream nazar-pc rev once # these abundance changes are merged. [patch."https://github.com/nazar-pc/abundance"] -ab-core-primitives = { git = "https://github.com/vedhavyas/abundance", rev = "834512648755f40a2aa0dc7d4fc44c66e2c3fa47" } -ab-proof-of-space = { git = "https://github.com/vedhavyas/abundance", rev = "834512648755f40a2aa0dc7d4fc44c66e2c3fa47" } -ab-proof-of-space-gpu = { git = "https://github.com/vedhavyas/abundance", rev = "834512648755f40a2aa0dc7d4fc44c66e2c3fa47" } +ab-core-primitives = { git = "https://github.com/vedhavyas/abundance", rev = "6d5c97150b639a8fae80683b180b84c11453545e" } +ab-proof-of-space = { git = "https://github.com/vedhavyas/abundance", rev = "6d5c97150b639a8fae80683b180b84c11453545e" } +ab-proof-of-space-gpu = { git = "https://github.com/vedhavyas/abundance", rev = "6d5c97150b639a8fae80683b180b84c11453545e" } From 3f5813de6cbce04feefad278ffe864d4abb0442a Mon Sep 17 00:00:00 2001 From: Vedhavyas Singareddi <7549475+vedhavyas@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:02:05 +0530 Subject: [PATCH 8/8] add wgpu byte-identity test against CPU ChiaV2Table --- crates/subspace-farmer-components/Cargo.toml | 2 + .../tests/plot_read_roundtrip.rs | 309 +++++++++++++++++- 2 files changed, 307 insertions(+), 4 deletions(-) diff --git a/crates/subspace-farmer-components/Cargo.toml b/crates/subspace-farmer-components/Cargo.toml index 1d6afbe3cba..fdfc4fedab7 100644 --- a/crates/subspace-farmer-components/Cargo.toml +++ b/crates/subspace-farmer-components/Cargo.toml @@ -46,10 +46,12 @@ tracing.workspace = true winapi = "0.3.9" [dev-dependencies] +anyhow.workspace = true criterion.workspace = true futures.workspace = true subspace-archiving.workspace = true subspace-proof-of-space.workspace = true +subspace-proof-of-space-wgpu.workspace = true [[bench]] name = "plotting" diff --git a/crates/subspace-farmer-components/tests/plot_read_roundtrip.rs b/crates/subspace-farmer-components/tests/plot_read_roundtrip.rs index 399f7b52fb4..60a9d5534b4 100644 --- a/crates/subspace-farmer-components/tests/plot_read_roundtrip.rs +++ b/crates/subspace-farmer-components/tests/plot_read_roundtrip.rs @@ -1,21 +1,29 @@ -//! Plot a sector with the abundance-backed table and read a piece back unchanged. +//! Plot sectors with the old and abundance-backed tables and read pieces back, including under +//! the farmer's per-sector cutover dispatch. use futures::executor::block_on; use rand::prelude::*; -use std::num::{NonZeroU64, NonZeroUsize}; +use std::num::{NonZeroU8, NonZeroU64, NonZeroUsize}; use std::slice; +use std::sync::atomic::AtomicBool; use subspace_archiving::archiver::Archiver; use subspace_core_primitives::PublicKey; use subspace_core_primitives::pieces::{PieceOffset, Record}; +use subspace_core_primitives::sectors::SectorId; use subspace_core_primitives::segments::{HistorySize, RecordedHistorySegment}; use subspace_data_retrieval::piece_getter::PieceGetter; use subspace_erasure_coding::ErasureCoding; -use subspace_farmer_components::plotting::{CpuRecordsEncoder, PlotSectorOptions, plot_sector}; +use subspace_farmer_components::plotting::{ + CpuRecordsEncoder, PlotSectorOptions, RecordsEncoder, plot_sector, +}; use subspace_farmer_components::reading::{ReadSectorRecordChunksMode, read_piece}; +use subspace_farmer_components::sector::SectorContentsMap; use subspace_farmer_components::{FarmerProtocolInfo, ReadAt}; use subspace_kzg::Kzg; -use subspace_proof_of_space::Table; +use subspace_proof_of_space::chia::ChiaTable; use subspace_proof_of_space::chia_v2::ChiaV2Table; +use subspace_proof_of_space::{PosTable, Table}; +use subspace_proof_of_space_wgpu::{Device, WgpuDevice}; #[test] fn abundance_plot_read_roundtrip() { @@ -99,3 +107,296 @@ fn abundance_plot_read_roundtrip() { "record read back from an abundance-plotted sector must match the original" ); } + +/// Plot one sector with the old `ChiaTable` and one with the abundance `ChiaV2Table`, then read +/// both back selecting the table per sector via the farmer's cutover dispatch +/// (`PosTable::generator_for(is_post_cutover)`), exactly as an upgraded farm does. +#[test] +fn mixed_old_new_sectors_read_under_cutover_dispatch() { + let pieces_in_sector = 10; + let public_key = PublicKey::default(); + + let mut input = RecordedHistorySegment::new_boxed(); + StdRng::seed_from_u64(42).fill(AsMut::<[u8]>::as_mut(input.as_mut())); + let kzg = Kzg::new(); + let erasure_coding = ErasureCoding::new( + NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize) + .expect("Not zero; qed"), + ) + .unwrap(); + let mut archiver = Archiver::new(kzg.clone(), erasure_coding.clone()); + let archived_history_segment = archiver + .add_block( + AsRef::<[u8]>::as_ref(input.as_ref()).to_vec(), + Default::default(), + true, + ) + .archived_segments + .into_iter() + .next() + .unwrap(); + + let farmer_protocol_info = FarmerProtocolInfo { + history_size: HistorySize::from(NonZeroU64::new(1).unwrap()), + max_pieces_in_sector: pieces_in_sector, + recent_segments: HistorySize::from(NonZeroU64::new(5).unwrap()), + recent_history_fraction: ( + HistorySize::from(NonZeroU64::new(1).unwrap()), + HistorySize::from(NonZeroU64::new(10).unwrap()), + ), + min_sector_lifetime: HistorySize::from(NonZeroU64::new(4).unwrap()), + }; + + // "Old" sector plotted with the old table, "new" sector with the abundance table. + let mut old_sector = Vec::new(); + let mut old_generator = ChiaTable::generator(); + let old_plotted = block_on(plot_sector(PlotSectorOptions { + public_key: &public_key, + sector_index: 0, + piece_getter: &archived_history_segment, + farmer_protocol_info, + kzg: &kzg, + erasure_coding: &erasure_coding, + pieces_in_sector, + sector_output: &mut old_sector, + downloading_semaphore: None, + encoding_semaphore: None, + records_encoder: &mut CpuRecordsEncoder::::new( + slice::from_mut(&mut old_generator), + &erasure_coding, + &Default::default(), + ), + abort_early: &Default::default(), + })) + .unwrap(); + + let mut new_sector = Vec::new(); + let mut new_generator = ChiaV2Table::generator(); + let new_plotted = block_on(plot_sector(PlotSectorOptions { + public_key: &public_key, + sector_index: 1, + piece_getter: &archived_history_segment, + farmer_protocol_info, + kzg: &kzg, + erasure_coding: &erasure_coding, + pieces_in_sector, + sector_output: &mut new_sector, + downloading_semaphore: None, + encoding_semaphore: None, + records_encoder: &mut CpuRecordsEncoder::::new( + slice::from_mut(&mut new_generator), + &erasure_coding, + &Default::default(), + ), + abort_early: &Default::default(), + })) + .unwrap(); + + // Cutover sits between the two sectors: old_history <= cutover < new_history. The farmer's rule + // is `is_post_cutover = history_size > cutover`. + let cutover = 5u64; + let old_is_post_cutover = 3u64 > cutover; // false -> old table + let new_is_post_cutover = 7u64 > cutover; // true -> abundance table + + let piece_offset = PieceOffset::ZERO; + + // Pre-cutover sector must read back via the old table. + let old_expected = block_on( + archived_history_segment.get_piece(old_plotted.piece_indexes[usize::from(piece_offset)]), + ) + .unwrap() + .unwrap(); + let mut old_dispatch = PosTable::generator_for(old_is_post_cutover); + let old_read = block_on(read_piece::( + piece_offset, + &old_plotted.sector_id, + &old_plotted.sector_metadata, + &ReadAt::from_sync(old_sector.as_slice()), + &erasure_coding, + ReadSectorRecordChunksMode::ConcurrentChunks, + &mut old_dispatch, + )) + .unwrap(); + assert_eq!( + old_read.record(), + old_expected.record(), + "pre-cutover sector must read back via the old table under dispatch" + ); + + // Post-cutover sector must read back via the abundance table. + let new_expected = block_on( + archived_history_segment.get_piece(new_plotted.piece_indexes[usize::from(piece_offset)]), + ) + .unwrap() + .unwrap(); + let mut new_dispatch = PosTable::generator_for(new_is_post_cutover); + let new_read = block_on(read_piece::( + piece_offset, + &new_plotted.sector_id, + &new_plotted.sector_metadata, + &ReadAt::from_sync(new_sector.as_slice()), + &erasure_coding, + ReadSectorRecordChunksMode::ConcurrentChunks, + &mut new_dispatch, + )) + .unwrap(); + assert_eq!( + new_read.record(), + new_expected.record(), + "post-cutover sector must read back via the abundance table under dispatch" + ); + + // Misdispatch guard: reading the old sector with the abundance table must not reproduce it, so + // the per-sector dispatch is genuinely load-bearing (not both tables reading everything alike). + let mut wrong_generator = PosTable::generator_for(true); + let old_misread = block_on(read_piece::( + piece_offset, + &old_plotted.sector_id, + &old_plotted.sector_metadata, + &ReadAt::from_sync(old_sector.as_slice()), + &erasure_coding, + ReadSectorRecordChunksMode::ConcurrentChunks, + &mut wrong_generator, + )); + assert!( + old_misread + .map(|read| read.record() != old_expected.record()) + .unwrap_or(true), + "old sector read with the abundance table must not reproduce the original" + ); +} + +/// Minimal single-threaded [`RecordsEncoder`] over the production [`WgpuDevice`], used only to drive +/// `plot_sector` in the byte-identity test (the threaded `WgpuRecordsEncoder` lives in +/// `subspace-farmer`, which this crate cannot depend on). +struct WgpuTestEncoder { + device: WgpuDevice, +} + +impl RecordsEncoder for WgpuTestEncoder { + fn encode_records( + &mut self, + sector_id: &SectorId, + records: &mut [Record], + _abort_early: &AtomicBool, + ) -> anyhow::Result { + let pieces_in_sector = records + .len() + .try_into() + .map_err(|error| anyhow::anyhow!("Failed to convert pieces in sector: {error}"))?; + let mut sector_contents_map = SectorContentsMap::new(pieces_in_sector); + for ((piece_offset, record), mut encoded_chunks_used) in (PieceOffset::ZERO..) + .zip(records.iter_mut()) + .zip(sector_contents_map.iter_record_bitfields_mut()) + { + let pos_seed = sector_id.derive_evaluation_seed(piece_offset); + self.device + .generate_and_encode_pospace(&pos_seed, record, encoded_chunks_used.iter_mut()) + .map_err(|error| anyhow::anyhow!(error))?; + } + Ok(sector_contents_map) + } +} + +/// Byte-identity gate: a sector plotted with wgpu (GPU) must be byte-for-byte identical to one +/// plotted with the abundance CPU `ChiaV2Table`, so new GPU-plotted farms read and verify under the +/// same CPU path. Skips when no GPU is available (e.g. CI runners without a GPU). +#[test] +fn wgpu_plotted_sector_matches_cpu() { + let pieces_in_sector = 10; + let sector_index = 0; + let public_key = PublicKey::default(); + + let mut input = RecordedHistorySegment::new_boxed(); + StdRng::seed_from_u64(42).fill(AsMut::<[u8]>::as_mut(input.as_mut())); + let kzg = Kzg::new(); + let erasure_coding = ErasureCoding::new( + NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize) + .expect("Not zero; qed"), + ) + .unwrap(); + let mut archiver = Archiver::new(kzg.clone(), erasure_coding.clone()); + let archived_history_segment = archiver + .add_block( + AsRef::<[u8]>::as_ref(input.as_ref()).to_vec(), + Default::default(), + true, + ) + .archived_segments + .into_iter() + .next() + .unwrap(); + + let farmer_protocol_info = FarmerProtocolInfo { + history_size: HistorySize::from(NonZeroU64::new(1).unwrap()), + max_pieces_in_sector: pieces_in_sector, + recent_segments: HistorySize::from(NonZeroU64::new(5).unwrap()), + recent_history_fraction: ( + HistorySize::from(NonZeroU64::new(1).unwrap()), + HistorySize::from(NonZeroU64::new(10).unwrap()), + ), + min_sector_lifetime: HistorySize::from(NonZeroU64::new(4).unwrap()), + }; + + // One queue is enough for the test; skip entirely when no GPU is present. + let Some(wgpu_device) = block_on(Device::enumerate(|_device_type| { + NonZeroU8::new(1).expect("Not zero; qed") + })) + .into_iter() + .next() else { + eprintln!("No wgpu GPU available; skipping wgpu byte-identity gate"); + return; + }; + let instance = wgpu_device + .create_proofs_encoder_instances(true) + .into_iter() + .next() + .expect("At least one queue per device; qed"); + let mut wgpu_encoder = WgpuTestEncoder { + device: WgpuDevice::new(instance, erasure_coding.clone()), + }; + + let mut cpu_sector = Vec::new(); + let mut cpu_generator = ChiaV2Table::generator(); + block_on(plot_sector(PlotSectorOptions { + public_key: &public_key, + sector_index, + piece_getter: &archived_history_segment, + farmer_protocol_info, + kzg: &kzg, + erasure_coding: &erasure_coding, + pieces_in_sector, + sector_output: &mut cpu_sector, + downloading_semaphore: None, + encoding_semaphore: None, + records_encoder: &mut CpuRecordsEncoder::::new( + slice::from_mut(&mut cpu_generator), + &erasure_coding, + &Default::default(), + ), + abort_early: &Default::default(), + })) + .unwrap(); + + let mut wgpu_sector = Vec::new(); + block_on(plot_sector(PlotSectorOptions { + public_key: &public_key, + sector_index, + piece_getter: &archived_history_segment, + farmer_protocol_info, + kzg: &kzg, + erasure_coding: &erasure_coding, + pieces_in_sector, + sector_output: &mut wgpu_sector, + downloading_semaphore: None, + encoding_semaphore: None, + records_encoder: &mut wgpu_encoder, + abort_early: &Default::default(), + })) + .unwrap(); + + assert_eq!( + cpu_sector, wgpu_sector, + "wgpu-plotted sector must be byte-identical to the abundance CPU ChiaV2Table plot" + ); +}