1use crate::{Opcode, Operand, RegistersCircuit, RegistersTrait, StackTrait};
17use console::{
18 network::prelude::*,
19 program::{Literal, LiteralType, Plaintext, PlaintextType, Register, RegisterType, Scalar, Value},
20};
21
22pub type CommitBHP256<N> = CommitInstruction<N, { CommitVariant::CommitBHP256 as u8 }>;
24pub type CommitBHP512<N> = CommitInstruction<N, { CommitVariant::CommitBHP512 as u8 }>;
26pub type CommitBHP768<N> = CommitInstruction<N, { CommitVariant::CommitBHP768 as u8 }>;
28pub type CommitBHP1024<N> = CommitInstruction<N, { CommitVariant::CommitBHP1024 as u8 }>;
30
31pub type CommitPED64<N> = CommitInstruction<N, { CommitVariant::CommitPED64 as u8 }>;
33pub type CommitPED128<N> = CommitInstruction<N, { CommitVariant::CommitPED128 as u8 }>;
35
36#[derive(Debug, Clone, Eq, PartialEq)]
38pub enum CommitVariant {
39 CommitBHP256,
40 CommitBHP512,
41 CommitBHP768,
42 CommitBHP1024,
43 CommitPED64,
44 CommitPED128,
45}
46
47impl CommitVariant {
48 pub const fn opcode(variant: u8) -> &'static str {
50 match variant {
51 0 => "commit.bhp256",
52 1 => "commit.bhp512",
53 2 => "commit.bhp768",
54 3 => "commit.bhp1024",
55 4 => "commit.ped64",
56 5 => "commit.ped128",
57 6.. => panic!("Invalid 'commit' instruction opcode"),
58 }
59 }
60}
61
62fn is_valid_destination_type(destination_type: LiteralType) -> bool {
64 matches!(destination_type, LiteralType::Address | LiteralType::Field | LiteralType::Group)
65}
66
67#[derive(Clone, PartialEq, Eq, Hash)]
69pub struct CommitInstruction<N: Network, const VARIANT: u8> {
70 operands: Vec<Operand<N>>,
72 destination: Register<N>,
74 destination_type: LiteralType,
76}
77
78impl<N: Network, const VARIANT: u8> CommitInstruction<N, VARIANT> {
79 #[inline]
81 pub fn new(operands: Vec<Operand<N>>, destination: Register<N>, destination_type: LiteralType) -> Result<Self> {
82 ensure!(operands.len() == 2, "Commit instructions must have two operands");
84 ensure!(is_valid_destination_type(destination_type), "Invalid destination type for 'commit' instruction");
86 Ok(Self { operands, destination, destination_type })
88 }
89
90 #[inline]
92 pub const fn opcode() -> Opcode {
93 Opcode::Commit(CommitVariant::opcode(VARIANT))
94 }
95
96 #[inline]
98 pub fn operands(&self) -> &[Operand<N>] {
99 debug_assert!(self.operands.len() == 2, "Commit operations must have two operands");
101 &self.operands
103 }
104
105 #[inline]
107 pub fn destinations(&self) -> Vec<Register<N>> {
108 vec![self.destination.clone()]
109 }
110
111 #[inline]
113 pub const fn destination_type(&self) -> LiteralType {
114 self.destination_type
115 }
116
117 #[inline]
119 pub fn contains_external_struct(&self) -> bool {
120 false
121 }
122}
123
124macro_rules! do_commit {
130 ($N: ident, $variant: expr, $destination_type: expr, $input: expr, $randomizer: expr, $ty: ty, $q: expr) => {{
131 let func = match $variant {
132 0 => $N::commit_to_group_bhp256,
133 1 => $N::commit_to_group_bhp512,
134 2 => $N::commit_to_group_bhp768,
135 3 => $N::commit_to_group_bhp1024,
136 4 => $N::commit_to_group_ped64,
137 5 => $N::commit_to_group_ped128,
138 6.. => bail!("Invalid 'commit' variant: {}", $variant),
139 };
140
141 let literal_output: $ty = $q(func(&$input.to_bits_le(), $randomizer))?.into();
142 literal_output.cast_lossy($destination_type)?
143 }};
144}
145
146pub fn evaluate_commit<N: Network>(
151 variant: CommitVariant,
152 input: &Value<N>,
153 randomizer: &Scalar<N>,
154 destination_type: LiteralType,
155) -> Result<Literal<N>> {
156 evaluate_commit_internal(variant as u8, input, randomizer, destination_type)
157}
158
159fn evaluate_commit_internal<N: Network>(
160 variant: u8,
161 input: &Value<N>,
162 randomizer: &Scalar<N>,
163 destination_type: LiteralType,
164) -> Result<Literal<N>> {
165 Ok(do_commit!(N, variant, destination_type, input, randomizer, Literal<N>, |x| x))
166}
167
168impl<N: Network, const VARIANT: u8> CommitInstruction<N, VARIANT> {
169 pub fn evaluate(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
171 if self.operands.len() != 2 {
173 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
174 }
175 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
177
178 let input = registers.load(stack, &self.operands[0])?;
180 let randomizer = registers.load(stack, &self.operands[1])?;
181 let randomizer = match randomizer {
183 Value::Plaintext(Plaintext::Literal(Literal::Scalar(randomizer), ..)) => randomizer,
184 _ => bail!("Invalid randomizer type for the commit evaluation, expected a scalar"),
185 };
186
187 let output = evaluate_commit_internal(VARIANT, &input, &randomizer, self.destination_type)?;
188
189 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(output)))
191 }
192
193 pub fn execute<A: circuit::Aleo<Network = N>>(
195 &self,
196 stack: &impl StackTrait<N>,
197 registers: &mut impl RegistersCircuit<N, A>,
198 ) -> Result<()> {
199 use circuit::traits::ToBits;
200
201 if self.operands.len() != 2 {
203 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
204 }
205 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
207
208 let input = registers.load_circuit(stack, &self.operands[0])?;
210 let randomizer = registers.load_circuit(stack, &self.operands[1])?;
211 let randomizer = match randomizer {
213 circuit::Value::Plaintext(circuit::Plaintext::Literal(circuit::Literal::Scalar(randomizer), ..)) => {
214 randomizer
215 }
216 _ => bail!("Invalid randomizer type for the commit execution, expected a scalar"),
217 };
218
219 let output =
220 do_commit!(A, VARIANT, self.destination_type, &input, &randomizer, circuit::Literal<A>, Result::<_>::Ok);
221
222 let output = circuit::Value::Plaintext(circuit::Plaintext::Literal(output, Default::default()));
224 registers.store_circuit(stack, &self.destination, output)
226 }
227
228 #[inline]
230 pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
231 self.evaluate(stack, registers)
232 }
233
234 pub fn output_types(
236 &self,
237 _stack: &impl StackTrait<N>,
238 input_types: &[RegisterType<N>],
239 ) -> Result<Vec<RegisterType<N>>> {
240 if input_types.len() != 2 {
242 bail!("Instruction '{}' expects 2 inputs, found {} inputs", Self::opcode(), input_types.len())
243 }
244 if self.operands.len() != 2 {
246 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
247 }
248 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
250
251 match VARIANT {
254 0..=5 => Ok(vec![RegisterType::Plaintext(PlaintextType::Literal(self.destination_type))]),
255 6.. => bail!("Invalid 'commit' variant: {VARIANT}"),
256 }
257 }
258}
259
260impl<N: Network, const VARIANT: u8> Parser for CommitInstruction<N, VARIANT> {
261 fn parse(string: &str) -> ParserResult<Self> {
263 let (string, _) = tag(*Self::opcode())(string)?;
265 let (string, _) = Sanitizer::parse_whitespaces(string)?;
267 let (string, first) = Operand::parse(string)?;
269 let (string, _) = Sanitizer::parse_whitespaces(string)?;
271 let (string, second) = Operand::parse(string)?;
273 let (string, _) = Sanitizer::parse_whitespaces(string)?;
275 let (string, _) = tag("into")(string)?;
277 let (string, _) = Sanitizer::parse_whitespaces(string)?;
279 let (string, destination) = Register::parse(string)?;
281 let (string, _) = Sanitizer::parse_whitespaces(string)?;
283 let (string, _) = tag("as")(string)?;
285 let (string, _) = Sanitizer::parse_whitespaces(string)?;
287 let (string, destination_type) = LiteralType::parse(string)?;
289 match destination_type {
291 LiteralType::Address | LiteralType::Field | LiteralType::Group => {
292 Ok((string, Self { operands: vec![first, second], destination, destination_type }))
293 }
294 _ => map_res(fail, |_: ParserResult<Self>| {
295 Err(error(format!("Failed to parse 'commit': '{destination_type}' is invalid")))
296 })(string),
297 }
298 }
299}
300
301impl<N: Network, const VARIANT: u8> FromStr for CommitInstruction<N, VARIANT> {
302 type Err = Error;
303
304 #[inline]
306 fn from_str(string: &str) -> Result<Self> {
307 match Self::parse(string) {
308 Ok((remainder, object)) => {
309 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
311 Ok(object)
313 }
314 Err(error) => bail!("Failed to parse string. {error}"),
315 }
316 }
317}
318
319impl<N: Network, const VARIANT: u8> Debug for CommitInstruction<N, VARIANT> {
320 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
322 Display::fmt(self, f)
323 }
324}
325
326impl<N: Network, const VARIANT: u8> Display for CommitInstruction<N, VARIANT> {
327 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
329 if self.operands.len() != 2 {
331 return Err(fmt::Error);
332 }
333 write!(f, "{} ", Self::opcode())?;
335 self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
336 write!(f, "into {} as {}", self.destination, self.destination_type)
337 }
338}
339
340impl<N: Network, const VARIANT: u8> FromBytes for CommitInstruction<N, VARIANT> {
341 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
343 let mut operands = Vec::with_capacity(2);
345 for _ in 0..2 {
347 operands.push(Operand::read_le(&mut reader)?);
348 }
349 let destination = Register::read_le(&mut reader)?;
351 let destination_type = LiteralType::read_le(&mut reader)?;
353
354 Self::new(operands, destination, destination_type).map_err(error)
356 }
357}
358
359impl<N: Network, const VARIANT: u8> ToBytes for CommitInstruction<N, VARIANT> {
360 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
362 if self.operands.len() != 2 {
364 return Err(error(format!("The number of operands must be 2, found {}", self.operands.len())));
365 }
366 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
368 self.destination.write_le(&mut writer)?;
370 self.destination_type.write_le(&mut writer)
372 }
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use console::network::MainnetV0;
379
380 type CurrentNetwork = MainnetV0;
381
382 fn valid_destination_types() -> &'static [LiteralType] {
384 &[LiteralType::Address, LiteralType::Field, LiteralType::Group]
385 }
386
387 #[test]
388 fn test_parse() {
389 for destination_type in valid_destination_types() {
390 let instruction = format!("commit.bhp512 r0 r1 into r2 as {destination_type}");
391 let (string, commit) = CommitBHP512::<CurrentNetwork>::parse(&instruction).unwrap();
392 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
393 assert_eq!(commit.operands.len(), 2, "The number of operands is incorrect");
394 assert_eq!(commit.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
395 assert_eq!(commit.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
396 assert_eq!(commit.destination, Register::Locator(2), "The destination register is incorrect");
397 assert_eq!(commit.destination_type, *destination_type, "The destination type is incorrect");
398 }
399 }
400}