mod.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphavm library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 pub mod batched; 20 pub mod standard; 21 22 #[cfg(target_arch = "x86_64")] 23 pub mod prefetch; 24 25 use alphavm_curves::{bls12_377::G1Affine, traits::AffineCurve}; 26 use alphavm_fields::PrimeField; 27 28 use core::any::TypeId; 29 30 pub struct VariableBase; 31 32 impl VariableBase { 33 pub fn msm<G: AffineCurve>(bases: &[G], scalars: &[<G::ScalarField as PrimeField>::BigInteger]) -> G::Projective { 34 // For BLS12-377, we perform variable base MSM using a batched addition 35 // technique. 36 if TypeId::of::<G>() == TypeId::of::<G1Affine>() { 37 #[cfg(all(feature = "cuda", target_arch = "x86_64"))] 38 // TODO SNP: where to set the threshold 39 if scalars.len() > 1024 { 40 let result = alphavm_algorithms_cuda::msm::<G, G::Projective, <G::ScalarField as PrimeField>::BigInteger>( 41 bases, scalars, 42 ); 43 if let Ok(result) = result { 44 return result; 45 } 46 } 47 batched::msm(bases, scalars) 48 } 49 // For all other curves, we perform variable base MSM using Pippenger's algorithm. 50 else { 51 standard::msm(bases, scalars) 52 } 53 } 54 55 #[cfg(test)] 56 fn msm_naive<G: AffineCurve>(bases: &[G], scalars: &[<G::ScalarField as PrimeField>::BigInteger]) -> G::Projective { 57 use alphavm_utilities::BitIteratorBE; 58 use itertools::Itertools; 59 60 bases.iter().zip_eq(scalars).map(|(base, scalar)| base.mul_bits(BitIteratorBE::new(*scalar))).sum() 61 } 62 63 #[cfg(test)] 64 fn msm_naive_parallel<G: AffineCurve>( 65 bases: &[G], 66 scalars: &[<G::ScalarField as PrimeField>::BigInteger], 67 ) -> G::Projective { 68 use alphavm_utilities::BitIteratorBE; 69 use rayon::prelude::*; 70 71 bases.par_iter().zip_eq(scalars).map(|(base, scalar)| base.mul_bits(BitIteratorBE::new(*scalar))).sum() 72 } 73 } 74 75 #[cfg(test)] 76 mod tests { 77 use super::*; 78 use alphavm_curves::bls12_377::{Fr, G1Affine}; 79 use alphavm_fields::PrimeField; 80 use alphavm_utilities::rand::TestRng; 81 82 #[cfg(all(feature = "cuda", target_arch = "x86_64"))] 83 use alphavm_curves::ProjectiveCurve; 84 85 fn create_scalar_bases<G: AffineCurve<ScalarField = F>, F: PrimeField>( 86 rng: &mut TestRng, 87 size: usize, 88 ) -> (Vec<G>, Vec<F::BigInteger>) { 89 let bases = (0..size).map(|_| G::rand(rng)).collect::<Vec<_>>(); 90 let scalars = (0..size).map(|_| F::rand(rng).to_bigint()).collect::<Vec<_>>(); 91 (bases, scalars) 92 } 93 94 #[test] 95 fn test_msm() { 96 use alphavm_curves::ProjectiveCurve; 97 98 let mut rng = TestRng::default(); 99 for msm_size in [1, 5, 10, 50, 100, 500, 1000] { 100 let (bases, scalars) = create_scalar_bases::<G1Affine, Fr>(&mut rng, msm_size); 101 102 let naive_a = VariableBase::msm_naive(bases.as_slice(), scalars.as_slice()).to_affine(); 103 let naive_b = VariableBase::msm_naive_parallel(bases.as_slice(), scalars.as_slice()).to_affine(); 104 assert_eq!(naive_a, naive_b, "MSM size: {msm_size}"); 105 106 let candidate = standard::msm(bases.as_slice(), scalars.as_slice()).to_affine(); 107 assert_eq!(naive_a, candidate, "MSM size: {msm_size}"); 108 109 let candidate = batched::msm(bases.as_slice(), scalars.as_slice()).to_affine(); 110 assert_eq!(naive_a, candidate, "MSM size: {msm_size}"); 111 } 112 } 113 114 #[cfg(all(feature = "cuda", target_arch = "x86_64"))] 115 #[test] 116 fn test_msm_cuda() { 117 let mut rng = TestRng::default(); 118 for i in 2..17 { 119 let (bases, scalars) = create_scalar_bases::<G1Affine, Fr>(&mut rng, 1 << i); 120 let rust = standard::msm(bases.as_slice(), scalars.as_slice()); 121 let cuda = VariableBase::msm::<G1Affine>(bases.as_slice(), scalars.as_slice()); 122 assert_eq!(rust.to_affine(), cuda.to_affine()); 123 } 124 } 125 }