snarkvm_synthesizer_program/logic/command/
rand_chacha.rs1use crate::{
17 FinalizeRegistersState,
18 Opcode,
19 Operand,
20 traits::{RegistersLoad, RegistersStore, StackMatches, StackProgram},
21};
22use console::{
23 network::prelude::*,
24 program::{Literal, LiteralType, Plaintext, Register, Value},
25 types::{Address, Boolean, Field, Group, I8, I16, I32, I64, I128, Scalar, U8, U16, U32, U64, U128},
26};
27
28use rand::SeedableRng;
29
30pub const MAX_ADDITIONAL_SEEDS: usize = 2;
32
33#[derive(Clone, PartialEq, Eq, Hash)]
40pub struct RandChaCha<N: Network> {
41 operands: Vec<Operand<N>>,
43 destination: Register<N>,
45 destination_type: LiteralType,
47}
48
49impl<N: Network> RandChaCha<N> {
50 #[inline]
52 pub const fn opcode() -> Opcode {
53 Opcode::Command("rand.chacha")
54 }
55
56 #[inline]
58 pub fn operands(&self) -> Vec<Operand<N>> {
59 self.operands.clone()
60 }
61
62 #[inline]
64 pub const fn destination(&self) -> &Register<N> {
65 &self.destination
66 }
67
68 #[inline]
70 pub const fn destination_type(&self) -> LiteralType {
71 self.destination_type
72 }
73}
74
75impl<N: Network> RandChaCha<N> {
76 #[inline]
78 pub fn finalize(
79 &self,
80 stack: &(impl StackMatches<N> + StackProgram<N>),
81 registers: &mut (impl RegistersLoad<N> + RegistersStore<N> + FinalizeRegistersState<N>),
82 ) -> Result<()> {
83 if self.operands.len() > MAX_ADDITIONAL_SEEDS {
85 bail!("The number of operands must be <= {MAX_ADDITIONAL_SEEDS}")
86 }
87
88 let seeds: Vec<_> = self.operands.iter().map(|operand| registers.load(stack, operand)).try_collect()?;
90
91 let consensus_version = N::CONSENSUS_VERSION(registers.state().block_height())?;
95 let preimage = if (ConsensusVersion::V1..=ConsensusVersion::V2).contains(&consensus_version) {
96 to_bits_le![
97 registers.state().random_seed(),
98 **registers.transition_id(),
99 stack.program_id(),
100 registers.function_name(),
101 self.destination.locator(),
102 self.destination_type.type_id(),
103 seeds
104 ]
105 } else {
106 to_bits_le![
107 registers.state().random_seed(),
108 **registers.transition_id(),
109 stack.program_id(),
110 registers.function_name(),
111 registers.nonce(),
112 self.destination.locator(),
113 self.destination_type.type_id(),
114 seeds
115 ]
116 };
117
118 let digest = N::hash_bhp1024(&preimage)?.to_bytes_le()?;
120 ensure!(digest.len() == 32, "The digest for the ChaChaRng seed must be 32-bytes");
122
123 let mut chacha_seed = [0u8; 32];
125 chacha_seed.copy_from_slice(&digest[..32]);
126
127 let mut rng = rand_chacha::ChaCha20Rng::from_seed(chacha_seed);
129
130 let output = match self.destination_type {
132 LiteralType::Address => Literal::Address(Address::new(Group::rand(&mut rng))),
133 LiteralType::Boolean => Literal::Boolean(Boolean::rand(&mut rng)),
134 LiteralType::Field => Literal::Field(Field::rand(&mut rng)),
135 LiteralType::Group => Literal::Group(Group::rand(&mut rng)),
136 LiteralType::I8 => Literal::I8(I8::rand(&mut rng)),
137 LiteralType::I16 => Literal::I16(I16::rand(&mut rng)),
138 LiteralType::I32 => Literal::I32(I32::rand(&mut rng)),
139 LiteralType::I64 => Literal::I64(I64::rand(&mut rng)),
140 LiteralType::I128 => Literal::I128(I128::rand(&mut rng)),
141 LiteralType::U8 => Literal::U8(U8::rand(&mut rng)),
142 LiteralType::U16 => Literal::U16(U16::rand(&mut rng)),
143 LiteralType::U32 => Literal::U32(U32::rand(&mut rng)),
144 LiteralType::U64 => Literal::U64(U64::rand(&mut rng)),
145 LiteralType::U128 => Literal::U128(U128::rand(&mut rng)),
146 LiteralType::Scalar => Literal::Scalar(Scalar::rand(&mut rng)),
147 LiteralType::Signature => bail!("Cannot 'rand.chacha' into a 'signature'"),
148 LiteralType::String => bail!("Cannot 'rand.chacha' into a 'string'"),
149 };
150
151 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(output)))
153 }
154}
155
156impl<N: Network> Parser for RandChaCha<N> {
157 #[inline]
159 fn parse(string: &str) -> ParserResult<Self> {
160 fn parse_operand<N: Network>(string: &str) -> ParserResult<Operand<N>> {
162 let (string, _) = Sanitizer::parse_whitespaces(string)?;
164 Operand::parse(string)
166 }
167
168 let (string, _) = Sanitizer::parse(string)?;
170 let (string, _) = tag(*Self::opcode())(string)?;
172 let (string, operands) = many0(parse_operand)(string)?;
174
175 let (string, _) = Sanitizer::parse_whitespaces(string)?;
177 let (string, _) = tag("into")(string)?;
179 let (string, _) = Sanitizer::parse_whitespaces(string)?;
181 let (string, destination) = Register::parse(string)?;
183 let (string, _) = Sanitizer::parse_whitespaces(string)?;
185 let (string, _) = tag("as")(string)?;
187 let (string, _) = Sanitizer::parse_whitespaces(string)?;
189 let (string, destination_type) = LiteralType::parse(string)?;
191
192 let (string, _) = Sanitizer::parse_whitespaces(string)?;
194 let (string, _) = tag(";")(string)?;
196
197 if destination_type == LiteralType::String {
199 return map_res(fail, |_: ParserResult<Self>| {
200 Err(error(format!("Failed to parse 'rand.chacha': '{destination_type}' is invalid")))
201 })(string);
202 }
203
204 match operands.len() <= MAX_ADDITIONAL_SEEDS {
205 true => Ok((string, Self { operands, destination, destination_type })),
206 false => map_res(fail, |_: ParserResult<Self>| {
207 Err(error("Failed to parse 'rand.chacha' opcode: too many operands"))
208 })(string),
209 }
210 }
211}
212
213impl<N: Network> FromStr for RandChaCha<N> {
214 type Err = Error;
215
216 #[inline]
218 fn from_str(string: &str) -> Result<Self> {
219 match Self::parse(string) {
220 Ok((remainder, object)) => {
221 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
223 Ok(object)
225 }
226 Err(error) => bail!("Failed to parse string. {error}"),
227 }
228 }
229}
230
231impl<N: Network> Debug for RandChaCha<N> {
232 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
234 Display::fmt(self, f)
235 }
236}
237
238impl<N: Network> Display for RandChaCha<N> {
239 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
241 if self.operands.len() > MAX_ADDITIONAL_SEEDS {
243 return Err(fmt::Error);
244 }
245
246 write!(f, "{} ", Self::opcode())?;
248 self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
249 write!(f, "into {} as {};", self.destination, self.destination_type)
250 }
251}
252
253impl<N: Network> FromBytes for RandChaCha<N> {
254 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
256 let num_operands = u8::read_le(&mut reader)? as usize;
258
259 if num_operands > MAX_ADDITIONAL_SEEDS {
261 return Err(error(format!("The number of operands must be <= {MAX_ADDITIONAL_SEEDS}")));
262 }
263
264 let mut operands = Vec::with_capacity(num_operands);
266 for _ in 0..num_operands {
268 operands.push(Operand::read_le(&mut reader)?);
269 }
270
271 let destination = Register::read_le(&mut reader)?;
273 let destination_type = LiteralType::read_le(&mut reader)?;
275
276 if destination_type == LiteralType::String {
278 return Err(error(format!("Failed to parse 'rand.chacha': '{destination_type}' is invalid")));
279 }
280
281 Ok(Self { operands, destination, destination_type })
283 }
284}
285
286impl<N: Network> ToBytes for RandChaCha<N> {
287 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
289 if self.operands.len() > MAX_ADDITIONAL_SEEDS {
291 return Err(error(format!("The number of operands must be <= {MAX_ADDITIONAL_SEEDS}")));
292 }
293
294 u8::try_from(self.operands.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
296 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
298 self.destination.write_le(&mut writer)?;
300 self.destination_type.write_le(&mut writer)
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use console::{network::MainnetV0, program::Register};
309
310 type CurrentNetwork = MainnetV0;
311
312 fn valid_destination_types() -> &'static [LiteralType] {
313 &[
314 LiteralType::Address,
315 LiteralType::Boolean,
316 LiteralType::Field,
317 LiteralType::Group,
318 LiteralType::I8,
319 LiteralType::I16,
320 LiteralType::I32,
321 LiteralType::I64,
322 LiteralType::I128,
323 LiteralType::U8,
324 LiteralType::U16,
325 LiteralType::U32,
326 LiteralType::U64,
327 LiteralType::U128,
328 LiteralType::Scalar,
329 ]
330 }
331
332 #[test]
333 fn test_parse() {
334 for destination_type in valid_destination_types() {
335 let instruction = format!("rand.chacha into r1 as {destination_type};");
336 let (string, rand) = RandChaCha::<CurrentNetwork>::parse(&instruction).unwrap();
337 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
338 assert_eq!(rand.operands.len(), 0, "The number of operands is incorrect");
339 assert_eq!(rand.destination, Register::Locator(1), "The destination is incorrect");
340 assert_eq!(rand.destination_type, *destination_type, "The destination type is incorrect");
341
342 let instruction = format!("rand.chacha r0 into r1 as {destination_type};");
343 let (string, rand) = RandChaCha::<CurrentNetwork>::parse(&instruction).unwrap();
344 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
345 assert_eq!(rand.operands.len(), 1, "The number of operands is incorrect");
346 assert_eq!(rand.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
347 assert_eq!(rand.destination, Register::Locator(1), "The second operand is incorrect");
348 assert_eq!(rand.destination_type, *destination_type, "The destination type is incorrect");
349
350 let instruction = format!("rand.chacha r0 r1 into r2 as {destination_type};");
351 let (string, rand) = RandChaCha::<CurrentNetwork>::parse(&instruction).unwrap();
352 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
353 assert_eq!(rand.operands.len(), 2, "The number of operands is incorrect");
354 assert_eq!(rand.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
355 assert_eq!(rand.operands[1], Operand::Register(Register::Locator(1)), "The first operand is incorrect");
356 assert_eq!(rand.destination, Register::Locator(2), "The second operand is incorrect");
357 assert_eq!(rand.destination_type, *destination_type, "The destination type is incorrect");
358 }
359 }
360}