diff --git a/crypto/math-cuda/src/fri.rs b/crypto/math-cuda/src/fri.rs index e3cadae1b..2615dc3d1 100644 --- a/crypto/math-cuda/src/fri.rs +++ b/crypto/math-cuda/src/fri.rs @@ -205,6 +205,10 @@ impl FriCommitState { num_leaves_u64, &mut leaves_view, )?, + DeviceHash::Rpo256 | DeviceHash::Rpx256 | DeviceHash::Poseidon => unimplemented!( + "{:?} device commit not yet ported (FRI layer ext3 leaves)", + self.hash + ), } } match self.hash { @@ -220,6 +224,10 @@ impl FriCommitState { &mut nodes_dev, num_leaves, )?, + DeviceHash::Rpo256 | DeviceHash::Rpx256 | DeviceHash::Poseidon => unimplemented!( + "{:?} device commit not yet ported (FRI layer inner tree levels)", + self.hash + ), } // Update inv_twiddles for the next layer: `new[j] = old[2j]^2` for diff --git a/crypto/math-cuda/src/lde.rs b/crypto/math-cuda/src/lde.rs index de3be1c74..33c45e557 100644 --- a/crypto/math-cuda/src/lde.rs +++ b/crypto/math-cuda/src/lde.rs @@ -1054,6 +1054,9 @@ fn build_inner_tree_levels_for( DeviceHash::Blake3 => { crate::blake3::build_inner_tree_levels(stream, be, nodes_dev, leaves_len) } + DeviceHash::Rpo256 | DeviceHash::Rpx256 | DeviceHash::Poseidon => { + unimplemented!("{hash:?} device commit not yet ported (inner tree levels)") + } } } @@ -1138,6 +1141,9 @@ fn coset_lde_row_major_inner( log_lde, &mut leaves_view, )?, + DeviceHash::Rpo256 | DeviceHash::Rpx256 | DeviceHash::Poseidon => { + unimplemented!("{hash:?} device commit not yet ported (row-major row-pair leaves)") + } } } build_inner_tree_levels_for(hash, stream.as_ref(), be, &mut nodes_dev, num_leaves)?; @@ -1333,6 +1339,9 @@ pub fn coset_lde_row_major_split_trees( log_lde, &mut leaves_view, )?, + DeviceHash::Rpo256 | DeviceHash::Rpx256 | DeviceHash::Poseidon => unimplemented!( + "{hash:?} device commit not yet ported (row-major row-pair leaves, column range)" + ), } } build_inner_tree_levels_for(hash, stream.as_ref(), be, &mut nodes_dev, num_leaves)?; @@ -2145,6 +2154,9 @@ fn coset_lde_batch_base_into_with_merkle_tree_inner( lde_u64, &mut leaves_view, )?, + (DeviceHash::Rpo256 | DeviceHash::Rpx256 | DeviceHash::Poseidon, _) => { + unimplemented!("{hash:?} device commit not yet ported (column-major base leaves)") + } } } @@ -2387,6 +2399,9 @@ fn evaluate_poly_coset_batch_ext3_into_inner( log_num_rows, &mut leaves_view, )?, + DeviceHash::Rpo256 | DeviceHash::Rpx256 | DeviceHash::Poseidon => { + unimplemented!("{hash:?} device commit not yet ported (comp-poly ext3 leaves)") + } } } build_inner_tree_levels_for(hash, stream.as_ref(), be, &mut nodes_dev, num_leaves)?; diff --git a/crypto/math-cuda/src/lib.rs b/crypto/math-cuda/src/lib.rs index 1ec415c5d..6470d5bb3 100644 --- a/crypto/math-cuda/src/lib.rs +++ b/crypto/math-cuda/src/lib.rs @@ -36,17 +36,35 @@ pub type Result = std::result::Result; /// /// The fused LDE+commit pipelines ([`lde`]), the composition-poly tree /// builders ([`merkle`] / [`blake3`]) and the FRI layer commits ([`fri`]) -/// each exist kernel-for-kernel in both families; this enum is the dispatch -/// key callers pass down. It deliberately carries no round counts or +/// each exist kernel-for-kernel in both BYTE families; this enum is the +/// dispatch key callers pass down. It deliberately carries no round counts or /// parameters: within one build each family is a single concrete hash /// (keccak-256, or `Blake3Chain` at the compiled round count), exactly as on /// the host. +/// +/// ★ The three ALGEBRAIC keys name hashes whose device kernels are not yet +/// ported. Every dispatch site in this crate carries an arm for them that +/// aborts with `unimplemented!` naming the hash — never an arm that launches a +/// byte-hash kernel in its place. The keys exist ahead of their kernels so the +/// host side (`stark::config::DeviceTreeBackend`) can name every commitment +/// hash under `cuda`: a tree labelled RPO is then built by RPO kernels or not +/// built at all. Porting a family means replacing those arms with launches, +/// and the set of arms is the checklist. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DeviceHash { /// Keccak-256 leaves and parents. Keccak256, /// `Blake3Chain` leaves and parents at the compiled round count. Blake3, + /// RPO256 leaves and parents. No device kernels yet: every dispatch site + /// aborts loudly on this key. + Rpo256, + /// RPX256 (XHash12) leaves and parents. No device kernels yet: every + /// dispatch site aborts loudly on this key. + Rpx256, + /// ⚠ Poseidon-original — UNSHIPPABLE on the host side too; present so the + /// key set mirrors `CommitmentHash` one-to-one. No device kernels. + Poseidon, } /// Toolchain sanity: plain wrapping u64 vector add. Not a field op. diff --git a/crypto/stark/src/config.rs b/crypto/stark/src/config.rs index 634f85e0c..5af566e1c 100644 --- a/crypto/stark/src/config.rs +++ b/crypto/stark/src/config.rs @@ -68,13 +68,20 @@ pub type FriLayerMerkleTree = MerkleTree>; /// 32-byte-node backend would compile there and hand back trees wearing a /// name whose hash the kernels never computed. /// -/// This trait closes that hole from both ends: `COMMITMENT_HASH` is the -/// dispatch key `gpu_lde` hands to `math-cuda` (selecting the keccak or the -/// BLAKE3 kernel family at every leaf, level and FRI-layer launch), and -/// implementing the trait is the reviewable claim that device kernels -/// producing exactly this backend's hash exist. A backend over some other -/// hash has no true constant to supply, so writing the impl is a deliberate -/// false statement rather than an omission nobody had to make. +/// This trait closes that hole: `COMMITMENT_HASH` is the dispatch key +/// `gpu_lde` hands to `math-cuda`, which selects the kernel family at every +/// leaf, level and FRI-layer launch — or, for a hash whose kernels are not yet +/// ported (the algebraic three), aborts at that launch with `unimplemented!` +/// naming the hash. Either way a tree labelled `Self` was hashed by `Self`'s +/// kernels or was not built at all; what the trait rules out is the third +/// outcome, a tree built by another hash's kernels and labelled `Self`. +/// +/// Implementing it is therefore the reviewable statement of WHICH hash the +/// device must compute for this backend, not a claim that it already can. A +/// backend whose hash no [`CommitmentHash`] variant names has no true constant +/// to supply, so writing the impl is a deliberate false statement rather than +/// an omission nobody had to make. The algebraic impls live beside their +/// backends in `prover::lfm::algebraic_commit`. pub trait DeviceTreeBackend: IsMerkleTreeBackend { /// The hash the device kernels must compute for trees labelled `Self`. const COMMITMENT_HASH: CommitmentHash; @@ -114,6 +121,12 @@ where /// artifacts name a hash. Every such match is a place that has to be revisited /// before this crate commits under a second hash; adding [`Self::Blake3`] broke /// them, which is what that mechanism is for. +/// +/// ★ Under `cuda` every variant also needs a `math_cuda::DeviceHash` twin. +/// `gpu_lde`'s bridge is total in both directions and asserts the pairing at +/// compile time, so a variant added here without one is a build error naming +/// the gap, and the bridge cannot cross-pair two hashes. A twin whose kernels +/// are not yet ported is legal: its dispatch arms abort loudly. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CommitmentHash { /// Keccak-256 at both the leaf and the parent layer. @@ -382,15 +395,19 @@ const _: fn() = || { /// [`DeviceTreeBackend`] impls above true statements rather than decoration — /// `gpu_lde` dispatches device kernels on that constant, so a mismatch here /// would be a GPU run hashing under a name the roots do not deserve. -const _: fn() = || { - fn assert_device_hash(expect: CommitmentHash) { - assert!(matches!( - (B::COMMITMENT_HASH, expect), - (CommitmentHash::Keccak256, CommitmentHash::Keccak256) - | (CommitmentHash::Blake3, CommitmentHash::Blake3) - )); +/// +/// A `const` block rather than a never-called closure, so the value comparison +/// is evaluated at compile time and not merely type-checked. Discriminants are +/// compared because `PartialEq` is not `const`. The algebraic configurations +/// carry the same check beside their impls in `prover::lfm::algebraic_commit`. +const _: () = { + const fn assert_device_hash(expect: CommitmentHash) { + assert!( + B::COMMITMENT_HASH as u8 == expect as u8, + "a configuration's member must name the configuration's own hash as its device key" + ); } - fn assert_same(_: core::marker::PhantomData<(T, T)>) {} + const fn assert_same(_: core::marker::PhantomData<(T, T)>) {} assert_device_hash::<::Batched>( CommitmentHash::Keccak256, diff --git a/crypto/stark/src/gpu_lde.rs b/crypto/stark/src/gpu_lde.rs index 53a48d19a..b5d5c7904 100644 --- a/crypto/stark/src/gpu_lde.rs +++ b/crypto/stark/src/gpu_lde.rs @@ -40,27 +40,73 @@ use rayon::prelude::{IndexedParallelIterator, ParallelIterator, ParallelSliceMut use crate::config::{Commitment, CommitmentHash, DeviceTreeBackend}; use crate::domain::Domain; use crate::fri::fri_commitment::FriLayer; +use crate::fri::fri_decommit::FriDecommitment; +use crate::trace::LDETraceTable; -/// The `math_cuda` dispatch key for `B`'s hash. -fn device_hash_of() -> math_cuda::DeviceHash { - match B::COMMITMENT_HASH { +/// The `math_cuda` dispatch key for a commitment hash. +/// +/// Total over [`CommitmentHash`], so a variant added there without a +/// [`math_cuda::DeviceHash`] twin fails to compile HERE, naming the gap, rather +/// than at whatever `match` first meets it. Every tree entry point below +/// dispatches on the result, and `math-cuda` either has kernels for the key or +/// aborts loudly at the first launch — a build must never quietly commit under +/// a hash the configuration did not name (HASH-PINNING.md), so there is no +/// byte-hash fallback anywhere on this path. +const fn device_hash_for(hash: CommitmentHash) -> math_cuda::DeviceHash { + match hash { CommitmentHash::Keccak256 => math_cuda::DeviceHash::Keccak256, CommitmentHash::Blake3 => math_cuda::DeviceHash::Blake3, - // The algebraic hashes have no device kernels yet. Loud by design: a - // build must never quietly commit under a hash the configuration did - // not name (HASH-PINNING.md), so there is no byte-hash fallback here. - // The per-table redo's dispatch lane replaces this arm with real - // `DeviceHash` variants once the kernels exist. - CommitmentHash::Rpo256 | CommitmentHash::Rpx256 | CommitmentHash::Poseidon => { - unimplemented!( - "{:?}: device Merkle commits are keccak/BLAKE3-only until the algebraic kernels land", - B::COMMITMENT_HASH - ) - } + CommitmentHash::Rpo256 => math_cuda::DeviceHash::Rpo256, + CommitmentHash::Rpx256 => math_cuda::DeviceHash::Rpx256, + CommitmentHash::Poseidon => math_cuda::DeviceHash::Poseidon, + } +} + +/// The inverse of [`device_hash_for`] — total over [`math_cuda::DeviceHash`], +/// so a device key no commitment hash names fails to compile here too. +const fn commitment_hash_for(hash: math_cuda::DeviceHash) -> CommitmentHash { + match hash { + math_cuda::DeviceHash::Keccak256 => CommitmentHash::Keccak256, + math_cuda::DeviceHash::Blake3 => CommitmentHash::Blake3, + math_cuda::DeviceHash::Rpo256 => CommitmentHash::Rpo256, + math_cuda::DeviceHash::Rpx256 => CommitmentHash::Rpx256, + math_cuda::DeviceHash::Poseidon => CommitmentHash::Poseidon, } } -use crate::fri::fri_decommit::FriDecommitment; -use crate::trace::LDETraceTable; + +/// ★ Every [`CommitmentHash`] variant has exactly one [`math_cuda::DeviceHash`] +/// twin, and the pairing is not crossed. +/// +/// The two `match`es above being total already proves each side maps +/// somewhere; this proves the two maps are inverse to each other, which is what +/// rules out a mis-paired arm (`Rpx256 => DeviceHash::Rpo256`) — the one +/// editing error that would hand a tree a name its kernels do not deserve once +/// the algebraic kernels exist. A sixth variant fails `device_hash_for` first +/// and is added to this list with its arm. Discriminants are compared because +/// `PartialEq` is not `const`. +const _: () = { + const ALL: [CommitmentHash; 5] = [ + CommitmentHash::Keccak256, + CommitmentHash::Blake3, + CommitmentHash::Rpo256, + CommitmentHash::Rpx256, + CommitmentHash::Poseidon, + ]; + let mut i = 0; + while i < ALL.len() { + let back = commitment_hash_for(device_hash_for(ALL[i])); + assert!( + back as u8 == ALL[i] as u8, + "a CommitmentHash must round-trip through its DeviceHash twin" + ); + i += 1; + } +}; + +/// The `math_cuda` dispatch key for `B`'s hash. +fn device_hash_of() -> math_cuda::DeviceHash { + device_hash_for(B::COMMITMENT_HASH) +} /// Break-even LDE size. For LDE sizes smaller than this, the CPU /// `coset_lde_full_expand` completes in a few hundred microseconds and the @@ -1376,6 +1422,12 @@ where math_cuda::DeviceHash::Blake3 => { math_cuda::blake3::build_comp_poly_tree_from_evals_ext3_keep(&raw_parts) } + math_cuda::DeviceHash::Rpo256 + | math_cuda::DeviceHash::Rpx256 + | math_cuda::DeviceHash::Poseidon => unimplemented!( + "{:?} device commit not yet ported (comp-poly tree from ext3 evals)", + B::COMMITMENT_HASH + ), } { Ok(t) => t, Err(_) => return None, @@ -1419,6 +1471,12 @@ where handle.m, handle.lde_size, ), + math_cuda::DeviceHash::Rpo256 + | math_cuda::DeviceHash::Rpx256 + | math_cuda::DeviceHash::Poseidon => unimplemented!( + "{:?} device commit not yet ported (comp-poly tree from resident slabs)", + B::COMMITMENT_HASH + ), } .ok()?; GPU_COMP_POLY_TREE_CALLS.fetch_add(1, Ordering::Relaxed); diff --git a/prover/src/hash_pin.rs b/prover/src/hash_pin.rs index 865686561..debe390bb 100644 --- a/prover/src/hash_pin.rs +++ b/prover/src/hash_pin.rs @@ -54,6 +54,18 @@ //! `IsStreamingLeafBackend` import in `proof_arena` were found. None of those //! three shows up on a build that only ever pins BLAKE3. //! +//! # `cuda` on an algebraic pin +//! +//! Compiles, and still cannot prove under the wrong hash. The algebraic +//! backends are `DeviceTreeBackend`s carrying their own `CommitmentHash` as the +//! device dispatch key, and `math-cuda` has no kernels for those permutations +//! yet, so a GPU run under an algebraic pin aborts at its first device commit +//! with `unimplemented!` naming the hash. ⛔ Neither a `compile_error!` nor a +//! byte-hash fallback belongs here: the first hides the cuda lint arm from the +//! branch, the second is exactly the silent wrong-hash build this module exists +//! to make impossible. Proving a block under an algebraic pin on GPU means +//! landing the kernels, and nothing less. +//! //! # ⚠ TWO regenerations, not one //! //! A pin change is **not** complete until every root blessed under the old hash diff --git a/prover/src/lfm/algebraic_commit.rs b/prover/src/lfm/algebraic_commit.rs index 4861bec46..7630183ae 100644 --- a/prover/src/lfm/algebraic_commit.rs +++ b/prover/src/lfm/algebraic_commit.rs @@ -64,7 +64,7 @@ use math::traits::AsBytes; use crypto::fiat_shamir::transcript_hash::TranscriptHash; use crypto::merkle_tree::traits::{IsLeafHasher, IsMerkleTreeBackend, IsStreamingLeafBackend}; -use stark::config::Commitment; +use stark::config::{Commitment, CommitmentHash, DeviceTreeBackend, StarkHash}; use super::hash::{HASH_STATE_FELTS, HasherKind, LfmHasher}; use super::rpo::{DOMAIN_LEAF, RATE_FELTS, domain_iv}; @@ -79,10 +79,16 @@ pub const DIGEST_FELTS: usize = 4; /// A type-level name for one algebraic permutation. /// /// The whole reason the backends below are one implementation: a candidate -/// joins by adding a unit struct and a `KIND`, and nothing else here moves. +/// joins by adding a unit struct, a `KIND` and a `COMMITMENT_HASH`, and +/// nothing else here moves. pub trait AlgebraicHasher: Clone + Copy + Default + Send + Sync + 'static { /// The permutation the `LFM_HASH` socket proves for this commitment. const KIND: HasherKind; + /// The name a root built by this permutation goes by — what both Merkle + /// families report as their device dispatch key and what the `StarkHash` + /// built on them reports as its `COMMITMENT_HASH`, stated once here so the + /// three cannot disagree. + const COMMITMENT_HASH: CommitmentHash; } /// Rescue-Prime Optimized. @@ -90,6 +96,7 @@ pub trait AlgebraicHasher: Clone + Copy + Default + Send + Sync + 'static { pub struct RpoCommit; impl AlgebraicHasher for RpoCommit { const KIND: HasherKind = HasherKind::Rpo; + const COMMITMENT_HASH: CommitmentHash = CommitmentHash::Rpo256; } /// Rescue-Prime eXtended (XHash12). @@ -97,6 +104,7 @@ impl AlgebraicHasher for RpoCommit { pub struct RpxCommit; impl AlgebraicHasher for RpxCommit { const KIND: HasherKind = HasherKind::Rpx; + const COMMITMENT_HASH: CommitmentHash = CommitmentHash::Rpx256; } /// ⚠ Poseidon-original — **UNSHIPPABLE** (broken family; eprint 2026/306 and @@ -106,6 +114,7 @@ impl AlgebraicHasher for RpxCommit { pub struct PoseidonCommit; impl AlgebraicHasher for PoseidonCommit { const KIND: HasherKind = HasherKind::Poseidon; + const COMMITMENT_HASH: CommitmentHash = CommitmentHash::Poseidon; } /// Four felts as a 32-byte `Commitment`, canonical big-endian. @@ -323,6 +332,32 @@ where } } +/// ★ The device marker — what makes an algebraic configuration expressible +/// under `cuda`. +/// +/// `StarkHash`'s cuda bounds require both families to be [`DeviceTreeBackend`]s, +/// and the constant is the dispatch key `gpu_lde` hands to `math-cuda`. No +/// device kernels exist for these permutations yet, so every `math-cuda` +/// dispatch site aborts with `unimplemented!` naming the hash the moment a +/// device commit is attempted under one of them — never a byte-hash kernel in +/// its place. That is the discipline HASH-PINNING.md demands: a build under an +/// algebraic pin exists and lints on both feature arms, and it still cannot +/// prove under a hash it is not named for. +impl DeviceTreeBackend for AlgebraicBatchBackend +where + Self: IsMerkleTreeBackend, + H: AlgebraicHasher, +{ + const COMMITMENT_HASH: CommitmentHash = H::COMMITMENT_HASH; +} +impl DeviceTreeBackend for AlgebraicPairBackend +where + Self: IsMerkleTreeBackend, + H: AlgebraicHasher, +{ + const COMMITMENT_HASH: CommitmentHash = H::COMMITMENT_HASH; +} + /// ★ A1 — the incremental leaf hasher, which BUFFERS. /// /// The padding flag is `len mod 8` and the sponge needs it in the capacity @@ -529,28 +564,21 @@ algebraic_transcript_hash!( /// convention: both are the generic algebraic backends at the same `H`, so a /// configuration mixing two permutations is not something to assert against — /// it is unspellable. -// Only the non-cuda build can express an algebraic configuration — see the -// macro's own note — so the imports it needs follow the same gate rather than -// sitting unused in a cuda build. -#[cfg(not(feature = "cuda"))] -use stark::config::{CommitmentHash, StarkHash}; - -/// ⚠ **NOT AVAILABLE UNDER `cuda`, and that is the `KeccakTreeBackend` marker -/// working rather than a gap.** A cuda build drives the whole commit phase on -/// device with the keccak kernels, so `StarkHash` there additionally requires -/// `Batched` and `Pair` to BE keccak backends — a bound these cannot satisfy and -/// must not. Under `cuda` an algebraic configuration is therefore not merely -/// unused, it is inexpressible, which is exactly the property that stops a build -/// producing keccak trees *labelled* RPO. `Blake3StarkHash` is gated the same way -/// and for the same reason; the algebraic path is CPU-only, as BLAKE3's already -/// is. +/// ★ **Expressible under `cuda`, and still unable to prove under the wrong +/// hash.** `StarkHash`'s cuda bounds require both families to be +/// [`DeviceTreeBackend`]s; the algebraic backends are, carrying the tag's own +/// [`CommitmentHash`] as the device dispatch key. `math-cuda` has no kernels for +/// these permutations yet, so a device commit under one of them aborts at the +/// first launch with `unimplemented!` naming the hash. The algebraic path is +/// CPU-only in fact, and a GPU run under it fails loudly rather than producing +/// byte-hash trees *labelled* RPO — the property HASH-PINNING.md demands, held +/// by the dispatch key rather than by forking the build. macro_rules! algebraic_stark_hash { - ($name:ident, $tag:ty, $transcript:ty, $commitment:expr, $doc:literal) => { + ($name:ident, $tag:ty, $transcript:ty, $doc:literal) => { #[doc = $doc] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct $name; - #[cfg(not(feature = "cuda"))] impl StarkHash for $name { type Batched = AlgebraicBatchBackend @@ -566,7 +594,11 @@ macro_rules! algebraic_stark_hash { type Transcript = $transcript; - const COMMITMENT_HASH: CommitmentHash = $commitment; + // Read from the tag rather than restated: it is the same constant + // the two families report as their device dispatch key, so the + // configuration's name and its kernels' name cannot be edited + // apart. + const COMMITMENT_HASH: CommitmentHash = <$tag as AlgebraicHasher>::COMMITMENT_HASH; } }; } @@ -575,21 +607,18 @@ algebraic_stark_hash!( RpoStarkHash, RpoCommit, RpoTranscriptHash, - CommitmentHash::Rpo256, "The RPO256 commitment configuration." ); algebraic_stark_hash!( RpxStarkHash, RpxCommit, RpxTranscriptHash, - CommitmentHash::Rpx256, "The RPX256 (XHash12) commitment configuration." ); algebraic_stark_hash!( PoseidonStarkHash, PoseidonCommit, PoseidonTranscriptHash, - CommitmentHash::Poseidon, "⚠ The Poseidon commitment configuration — UNSHIPPABLE, reference only." ); @@ -601,7 +630,6 @@ algebraic_stark_hash!( /// associated types are generic over `F`, and the bound that matters is the one /// the prover instantiates them at. This is that instantiation, as a compile-time /// check rather than as a comment claiming it holds. -#[cfg(not(feature = "cuda"))] const _: fn() = || { fn assert_usable() where @@ -618,6 +646,41 @@ const _: fn() = || { assert_usable::(); }; +/// ✓ Each configuration's members carry the configuration's own hash as their +/// device dispatch key — the tie that makes the [`DeviceTreeBackend`] impls +/// above true statements: `gpu_lde` dispatches kernels on that constant, so a +/// mismatch here would be a GPU run hashing under a name the roots do not +/// deserve. The same check `crypto/stark/src/config.rs` runs for the byte +/// configurations; discriminants are compared because `PartialEq` is not +/// `const`. +const _: () = { + const fn assert_device_hash(expect: CommitmentHash) { + assert!( + B::COMMITMENT_HASH as u8 == expect as u8, + "a configuration's member must name the configuration's own hash as its device key" + ); + } + + assert_device_hash::<::Batched>( + CommitmentHash::Rpo256, + ); + assert_device_hash::<::Pair>( + CommitmentHash::Rpo256, + ); + assert_device_hash::<::Batched>( + CommitmentHash::Rpx256, + ); + assert_device_hash::<::Pair>( + CommitmentHash::Rpx256, + ); + assert_device_hash::<::Batched>( + CommitmentHash::Poseidon, + ); + assert_device_hash::<::Pair>( + CommitmentHash::Poseidon, + ); +}; + #[cfg(test)] mod tests { use super::*; @@ -807,7 +870,6 @@ mod tests { /// Distinctness is asserted too: two configurations sharing a tag is the same /// failure with an extra step. #[test] - #[cfg(not(feature = "cuda"))] fn each_configuration_names_its_own_hash_and_tag() { use crate::lfm::statement::commitment_hash_tag;