mod.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the deltavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 mod hash_uncompressed; 17 18 use crate::Blake2Xs; 19 use deltavm_console_types::prelude::*; 20 use deltavm_utilities::BigInteger; 21 22 use serde::{Deserialize, Serialize}; 23 use std::sync::Arc; 24 25 /// The BHP chunk size (this implementation is for a 3-bit BHP). 26 pub(super) const BHP_CHUNK_SIZE: usize = 3; 27 pub(super) const BHP_LOOKUP_SIZE: usize = 1 << BHP_CHUNK_SIZE; 28 29 /// BHP is a collision-resistant hash function that takes a variable-length input. 30 /// The BHP hasher is used to process one internal iteration of the BHP hash function. 31 #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] 32 #[serde(bound = "E: Serialize + DeserializeOwned")] 33 pub struct BHPHasher<E: Environment, const NUM_WINDOWS: u8, const WINDOW_SIZE: u8> { 34 /// The bases for the BHP hash. 35 bases: Arc<Vec<Vec<Group<E>>>>, 36 /// The bases lookup table for the BHP hash. 37 bases_lookup: Arc<Vec<Vec<[Group<E>; BHP_LOOKUP_SIZE]>>>, 38 /// The random base for the BHP commitment. 39 random_base: Arc<Vec<Group<E>>>, 40 } 41 42 impl<E: Environment, const NUM_WINDOWS: u8, const WINDOW_SIZE: u8> BHPHasher<E, NUM_WINDOWS, WINDOW_SIZE> { 43 /// The maximum number of input bits. 44 const MAX_BITS: usize = NUM_WINDOWS as usize * WINDOW_SIZE as usize * BHP_CHUNK_SIZE; 45 /// The minimum number of input bits (at least one window). 46 const MIN_BITS: usize = WINDOW_SIZE as usize * BHP_CHUNK_SIZE; 47 48 /// Initializes a new instance of BHP with the given domain. 49 pub fn setup(domain: &str) -> Result<Self> { 50 // Calculate the maximum window size. 51 let mut maximum_window_size = 0; 52 let mut range = E::BigInteger::from(2_u64); 53 while range < E::Scalar::modulus_minus_one_div_two() { 54 // range < (p-1)/2 55 range.muln(4); // range * 2^4 56 maximum_window_size += 1; 57 } 58 ensure!(WINDOW_SIZE <= maximum_window_size, "The maximum BHP window size is {maximum_window_size}"); 59 60 // Compute the bases. 61 let bases = (0..NUM_WINDOWS) 62 .map(|index| { 63 // Construct an indexed message to attempt to sample a base. 64 let (generator, _, _) = Blake2Xs::hash_to_curve::<E::Affine>(&format!( 65 "Delta.BHP.{NUM_WINDOWS}.{WINDOW_SIZE}.{domain}.{index}" 66 )); 67 let mut base = Group::<E>::new(generator); 68 // Compute the generators for the sampled base. 69 let mut powers = Vec::with_capacity(WINDOW_SIZE as usize); 70 for _ in 0..WINDOW_SIZE { 71 powers.push(base); 72 for _ in 0..4 { 73 base = base.double(); 74 } 75 } 76 powers 77 }) 78 .collect::<Vec<Vec<Group<E>>>>(); 79 ensure!(bases.len() == NUM_WINDOWS as usize, "Incorrect number of BHP windows ({})", bases.len()); 80 for window in &bases { 81 ensure!(window.len() == WINDOW_SIZE as usize, "Incorrect BHP window size ({})", window.len()); 82 } 83 84 // Compute the bases lookup. 85 let bases_lookup = bases 86 .iter() 87 .map(|x| { 88 x.iter() 89 .map(|g| { 90 let mut lookup = [Group::<E>::zero(); BHP_LOOKUP_SIZE]; 91 for (i, element) in lookup.iter_mut().enumerate().take(BHP_LOOKUP_SIZE) { 92 *element = *g; 93 if (i & 0x01) != 0 { 94 *element += g; 95 } 96 if (i & 0x02) != 0 { 97 *element += g.double(); 98 } 99 if (i & 0x04) != 0 { 100 *element = element.neg(); 101 } 102 } 103 lookup 104 }) 105 .collect() 106 }) 107 .collect::<Vec<Vec<[Group<E>; BHP_LOOKUP_SIZE]>>>(); 108 ensure!(bases_lookup.len() == NUM_WINDOWS as usize, "Incorrect number of BHP lookups ({})", bases_lookup.len()); 109 for window in &bases_lookup { 110 ensure!(window.len() == WINDOW_SIZE as usize, "Incorrect BHP lookup window size ({})", window.len()); 111 } 112 113 // Next, compute the random base. 114 let (generator, _, _) = 115 Blake2Xs::hash_to_curve::<E::Affine>(&format!("Delta.BHP.{NUM_WINDOWS}.{WINDOW_SIZE}.{domain}.Randomizer")); 116 let mut base_power = Group::<E>::new(generator); 117 let mut random_base = Vec::with_capacity(Scalar::<E>::size_in_bits()); 118 for _ in 0..Scalar::<E>::size_in_bits() { 119 random_base.push(base_power); 120 base_power = base_power.double(); 121 } 122 ensure!( 123 random_base.len() == Scalar::<E>::size_in_bits(), 124 "Incorrect number of BHP random base powers ({})", 125 random_base.len() 126 ); 127 128 Ok(Self { bases: Arc::new(bases), bases_lookup: Arc::new(bases_lookup), random_base: Arc::new(random_base) }) 129 } 130 131 /// Returns the bases. 132 pub fn bases(&self) -> &Arc<Vec<Vec<Group<E>>>> { 133 &self.bases 134 } 135 136 /// Returns the random base window. 137 pub fn random_base(&self) -> &Arc<Vec<Group<E>>> { 138 &self.random_base 139 } 140 }