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
36pub enum CommitVariant {
38 CommitBHP256,
39 CommitBHP512,
40 CommitBHP768,
41 CommitBHP1024,
42 CommitPED64,
43 CommitPED128,
44}
45
46fn is_valid_destination_type(destination_type: LiteralType) -> bool {
48 matches!(destination_type, LiteralType::Address | LiteralType::Field | LiteralType::Group)
49}
50
51#[derive(Clone, PartialEq, Eq, Hash)]
53pub struct CommitInstruction<N: Network, const VARIANT: u8> {
54 operands: Vec<Operand<N>>,
56 destination: Register<N>,
58 destination_type: LiteralType,
60}
61
62impl<N: Network, const VARIANT: u8> CommitInstruction<N, VARIANT> {
63 #[inline]
65 pub fn new(operands: Vec<Operand<N>>, destination: Register<N>, destination_type: LiteralType) -> Result<Self> {
66 ensure!(operands.len() == 2, "Commit instructions must have two operands");
68 ensure!(is_valid_destination_type(destination_type), "Invalid destination type for 'commit' instruction");
70 Ok(Self { operands, destination, destination_type })
72 }
73
74 #[inline]
76 pub const fn opcode() -> Opcode {
77 match VARIANT {
78 0 => Opcode::Commit("commit.bhp256"),
79 1 => Opcode::Commit("commit.bhp512"),
80 2 => Opcode::Commit("commit.bhp768"),
81 3 => Opcode::Commit("commit.bhp1024"),
82 4 => Opcode::Commit("commit.ped64"),
83 5 => Opcode::Commit("commit.ped128"),
84 6.. => panic!("Invalid 'commit' instruction opcode"),
85 }
86 }
87
88 #[inline]
90 pub fn operands(&self) -> &[Operand<N>] {
91 debug_assert!(self.operands.len() == 2, "Commit operations must have two operands");
93 &self.operands
95 }
96
97 #[inline]
99 pub fn destinations(&self) -> Vec<Register<N>> {
100 vec![self.destination.clone()]
101 }
102
103 #[inline]
105 pub const fn destination_type(&self) -> LiteralType {
106 self.destination_type
107 }
108}
109
110macro_rules! do_commit {
116 ($N: ident, $variant: expr, $destination_type: expr, $input: expr, $randomizer: expr, $ty: ty, $q: expr) => {{
117 let func = match $variant {
118 0 => $N::commit_to_group_bhp256,
119 1 => $N::commit_to_group_bhp512,
120 2 => $N::commit_to_group_bhp768,
121 3 => $N::commit_to_group_bhp1024,
122 4 => $N::commit_to_group_ped64,
123 5 => $N::commit_to_group_ped128,
124 6.. => bail!("Invalid 'commit' variant: {}", $variant),
125 };
126
127 let literal_output: $ty = $q(func(&$input.to_bits_le(), $randomizer))?.into();
128 literal_output.cast_lossy($destination_type)?
129 }};
130}
131
132pub fn evaluate_commit<N: Network>(
137 variant: CommitVariant,
138 input: &Value<N>,
139 randomizer: &Scalar<N>,
140 destination_type: LiteralType,
141) -> Result<Literal<N>> {
142 evaluate_commit_internal(variant as u8, input, randomizer, destination_type)
143}
144
145fn evaluate_commit_internal<N: Network>(
146 variant: u8,
147 input: &Value<N>,
148 randomizer: &Scalar<N>,
149 destination_type: LiteralType,
150) -> Result<Literal<N>> {
151 Ok(do_commit!(N, variant, destination_type, input, randomizer, Literal<N>, |x| x))
152}
153
154impl<N: Network, const VARIANT: u8> CommitInstruction<N, VARIANT> {
155 pub fn evaluate(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
157 if self.operands.len() != 2 {
159 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
160 }
161 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
163
164 let input = registers.load(stack, &self.operands[0])?;
166 let randomizer = registers.load(stack, &self.operands[1])?;
167 let randomizer = match randomizer {
169 Value::Plaintext(Plaintext::Literal(Literal::Scalar(randomizer), ..)) => randomizer,
170 _ => bail!("Invalid randomizer type for the commit evaluation, expected a scalar"),
171 };
172
173 let output = evaluate_commit_internal(VARIANT, &input, &randomizer, self.destination_type)?;
174
175 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(output)))
177 }
178
179 pub fn execute<A: circuit::Aleo<Network = N>>(
181 &self,
182 stack: &impl StackTrait<N>,
183 registers: &mut impl RegistersCircuit<N, A>,
184 ) -> Result<()> {
185 use circuit::traits::ToBits;
186
187 if self.operands.len() != 2 {
189 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
190 }
191 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
193
194 let input = registers.load_circuit(stack, &self.operands[0])?;
196 let randomizer = registers.load_circuit(stack, &self.operands[1])?;
197 let randomizer = match randomizer {
199 circuit::Value::Plaintext(circuit::Plaintext::Literal(circuit::Literal::Scalar(randomizer), ..)) => {
200 randomizer
201 }
202 _ => bail!("Invalid randomizer type for the commit execution, expected a scalar"),
203 };
204
205 let output =
206 do_commit!(A, VARIANT, self.destination_type, &input, &randomizer, circuit::Literal<A>, Result::<_>::Ok);
207
208 let output = circuit::Value::Plaintext(circuit::Plaintext::Literal(output, Default::default()));
210 registers.store_circuit(stack, &self.destination, output)
212 }
213
214 #[inline]
216 pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
217 self.evaluate(stack, registers)
218 }
219
220 pub fn output_types(
222 &self,
223 _stack: &impl StackTrait<N>,
224 input_types: &[RegisterType<N>],
225 ) -> Result<Vec<RegisterType<N>>> {
226 if input_types.len() != 2 {
228 bail!("Instruction '{}' expects 2 inputs, found {} inputs", Self::opcode(), input_types.len())
229 }
230 if self.operands.len() != 2 {
232 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
233 }
234 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
236
237 match VARIANT {
240 0..=5 => Ok(vec![RegisterType::Plaintext(PlaintextType::Literal(self.destination_type))]),
241 6.. => bail!("Invalid 'commit' variant: {VARIANT}"),
242 }
243 }
244}
245
246impl<N: Network, const VARIANT: u8> Parser for CommitInstruction<N, VARIANT> {
247 fn parse(string: &str) -> ParserResult<Self> {
249 let (string, _) = tag(*Self::opcode())(string)?;
251 let (string, _) = Sanitizer::parse_whitespaces(string)?;
253 let (string, first) = Operand::parse(string)?;
255 let (string, _) = Sanitizer::parse_whitespaces(string)?;
257 let (string, second) = Operand::parse(string)?;
259 let (string, _) = Sanitizer::parse_whitespaces(string)?;
261 let (string, _) = tag("into")(string)?;
263 let (string, _) = Sanitizer::parse_whitespaces(string)?;
265 let (string, destination) = Register::parse(string)?;
267 let (string, _) = Sanitizer::parse_whitespaces(string)?;
269 let (string, _) = tag("as")(string)?;
271 let (string, _) = Sanitizer::parse_whitespaces(string)?;
273 let (string, destination_type) = LiteralType::parse(string)?;
275 match destination_type {
277 LiteralType::Address | LiteralType::Field | LiteralType::Group => {
278 Ok((string, Self { operands: vec![first, second], destination, destination_type }))
279 }
280 _ => map_res(fail, |_: ParserResult<Self>| {
281 Err(error(format!("Failed to parse 'commit': '{destination_type}' is invalid")))
282 })(string),
283 }
284 }
285}
286
287impl<N: Network, const VARIANT: u8> FromStr for CommitInstruction<N, VARIANT> {
288 type Err = Error;
289
290 #[inline]
292 fn from_str(string: &str) -> Result<Self> {
293 match Self::parse(string) {
294 Ok((remainder, object)) => {
295 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
297 Ok(object)
299 }
300 Err(error) => bail!("Failed to parse string. {error}"),
301 }
302 }
303}
304
305impl<N: Network, const VARIANT: u8> Debug for CommitInstruction<N, VARIANT> {
306 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
308 Display::fmt(self, f)
309 }
310}
311
312impl<N: Network, const VARIANT: u8> Display for CommitInstruction<N, VARIANT> {
313 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
315 if self.operands.len() != 2 {
317 return Err(fmt::Error);
318 }
319 write!(f, "{} ", Self::opcode())?;
321 self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
322 write!(f, "into {} as {}", self.destination, self.destination_type)
323 }
324}
325
326impl<N: Network, const VARIANT: u8> FromBytes for CommitInstruction<N, VARIANT> {
327 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
329 let mut operands = Vec::with_capacity(2);
331 for _ in 0..2 {
333 operands.push(Operand::read_le(&mut reader)?);
334 }
335 let destination = Register::read_le(&mut reader)?;
337 let destination_type = LiteralType::read_le(&mut reader)?;
339
340 Self::new(operands, destination, destination_type).map_err(error)
342 }
343}
344
345impl<N: Network, const VARIANT: u8> ToBytes for CommitInstruction<N, VARIANT> {
346 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
348 if self.operands.len() != 2 {
350 return Err(error(format!("The number of operands must be 2, found {}", self.operands.len())));
351 }
352 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
354 self.destination.write_le(&mut writer)?;
356 self.destination_type.write_le(&mut writer)
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use super::*;
364 use console::network::MainnetV0;
365
366 type CurrentNetwork = MainnetV0;
367
368 fn valid_destination_types() -> &'static [LiteralType] {
370 &[LiteralType::Address, LiteralType::Field, LiteralType::Group]
371 }
372
373 #[test]
374 fn test_parse() {
375 for destination_type in valid_destination_types() {
376 let instruction = format!("commit.bhp512 r0 r1 into r2 as {destination_type}");
377 let (string, commit) = CommitBHP512::<CurrentNetwork>::parse(&instruction).unwrap();
378 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
379 assert_eq!(commit.operands.len(), 2, "The number of operands is incorrect");
380 assert_eq!(commit.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
381 assert_eq!(commit.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
382 assert_eq!(commit.destination, Register::Locator(2), "The destination register is incorrect");
383 assert_eq!(commit.destination_type, *destination_type, "The destination type is incorrect");
384 }
385 }
386}