1use crate::{
17 Opcode,
18 Operand,
19 traits::{RegistersLoad, RegistersLoadCircuit, RegistersStore, RegistersStoreCircuit, StackMatches, StackProgram},
20};
21use console::{
22 network::prelude::*,
23 program::{Literal, LiteralType, Plaintext, PlaintextType, Register, RegisterType, Value},
24};
25
26pub type CommitBHP256<N> = CommitInstruction<N, { Committer::CommitBHP256 as u8 }>;
28pub type CommitBHP512<N> = CommitInstruction<N, { Committer::CommitBHP512 as u8 }>;
30pub type CommitBHP768<N> = CommitInstruction<N, { Committer::CommitBHP768 as u8 }>;
32pub type CommitBHP1024<N> = CommitInstruction<N, { Committer::CommitBHP1024 as u8 }>;
34
35pub type CommitPED64<N> = CommitInstruction<N, { Committer::CommitPED64 as u8 }>;
37pub type CommitPED128<N> = CommitInstruction<N, { Committer::CommitPED128 as u8 }>;
39
40enum Committer {
41 CommitBHP256,
42 CommitBHP512,
43 CommitBHP768,
44 CommitBHP1024,
45 CommitPED64,
46 CommitPED128,
47}
48
49fn is_valid_destination_type(destination_type: LiteralType) -> bool {
51 matches!(destination_type, LiteralType::Address | LiteralType::Field | LiteralType::Group)
52}
53
54#[derive(Clone, PartialEq, Eq, Hash)]
56pub struct CommitInstruction<N: Network, const VARIANT: u8> {
57 operands: Vec<Operand<N>>,
59 destination: Register<N>,
61 destination_type: LiteralType,
63}
64
65impl<N: Network, const VARIANT: u8> CommitInstruction<N, VARIANT> {
66 #[inline]
68 pub fn new(operands: Vec<Operand<N>>, destination: Register<N>, destination_type: LiteralType) -> Result<Self> {
69 ensure!(operands.len() == 2, "Commit instructions must have two operands");
71 ensure!(is_valid_destination_type(destination_type), "Invalid destination type for 'commit' instruction");
73 Ok(Self { operands, destination, destination_type })
75 }
76
77 #[inline]
79 pub const fn opcode() -> Opcode {
80 match VARIANT {
81 0 => Opcode::Commit("commit.bhp256"),
82 1 => Opcode::Commit("commit.bhp512"),
83 2 => Opcode::Commit("commit.bhp768"),
84 3 => Opcode::Commit("commit.bhp1024"),
85 4 => Opcode::Commit("commit.ped64"),
86 5 => Opcode::Commit("commit.ped128"),
87 6.. => panic!("Invalid 'commit' instruction opcode"),
88 }
89 }
90
91 #[inline]
93 pub fn operands(&self) -> &[Operand<N>] {
94 debug_assert!(self.operands.len() == 2, "Commit operations must have two operands");
96 &self.operands
98 }
99
100 #[inline]
102 pub fn destinations(&self) -> Vec<Register<N>> {
103 vec![self.destination.clone()]
104 }
105
106 #[inline]
108 pub const fn destination_type(&self) -> LiteralType {
109 self.destination_type
110 }
111}
112
113impl<N: Network, const VARIANT: u8> CommitInstruction<N, VARIANT> {
114 #[inline]
116 pub fn evaluate(
117 &self,
118 stack: &(impl StackMatches<N> + StackProgram<N>),
119 registers: &mut (impl RegistersLoad<N> + RegistersStore<N>),
120 ) -> Result<()> {
121 if self.operands.len() != 2 {
123 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
124 }
125 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
127
128 let input = registers.load(stack, &self.operands[0])?;
130 let randomizer = registers.load(stack, &self.operands[1])?;
131 let randomizer = match randomizer {
133 Value::Plaintext(Plaintext::Literal(Literal::Scalar(randomizer), ..)) => randomizer,
134 _ => bail!("Invalid randomizer type for the commit evaluation, expected a scalar"),
135 };
136
137 let output = match VARIANT {
139 0 => Literal::Group(N::commit_to_group_bhp256(&input.to_bits_le(), &randomizer)?),
140 1 => Literal::Group(N::commit_to_group_bhp512(&input.to_bits_le(), &randomizer)?),
141 2 => Literal::Group(N::commit_to_group_bhp768(&input.to_bits_le(), &randomizer)?),
142 3 => Literal::Group(N::commit_to_group_bhp1024(&input.to_bits_le(), &randomizer)?),
143 4 => Literal::Group(N::commit_to_group_ped64(&input.to_bits_le(), &randomizer)?),
144 5 => Literal::Group(N::commit_to_group_ped128(&input.to_bits_le(), &randomizer)?),
145 6.. => bail!("Invalid 'commit' variant: {VARIANT}"),
146 };
147 let output = output.cast_lossy(self.destination_type)?;
149 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(output)))
151 }
152
153 #[inline]
155 pub fn execute<A: circuit::Aleo<Network = N>>(
156 &self,
157 stack: &(impl StackMatches<N> + StackProgram<N>),
158 registers: &mut (impl RegistersLoadCircuit<N, A> + RegistersStoreCircuit<N, A>),
159 ) -> Result<()> {
160 use circuit::traits::ToBits;
161
162 if self.operands.len() != 2 {
164 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
165 }
166 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
168
169 let input = registers.load_circuit(stack, &self.operands[0])?;
171 let randomizer = registers.load_circuit(stack, &self.operands[1])?;
172 let randomizer = match randomizer {
174 circuit::Value::Plaintext(circuit::Plaintext::Literal(circuit::Literal::Scalar(randomizer), ..)) => {
175 randomizer
176 }
177 _ => bail!("Invalid randomizer type for the commit execution, expected a scalar"),
178 };
179
180 let output = match VARIANT {
182 0 => circuit::Literal::Group(A::commit_to_group_bhp256(&input.to_bits_le(), &randomizer)),
183 1 => circuit::Literal::Group(A::commit_to_group_bhp512(&input.to_bits_le(), &randomizer)),
184 2 => circuit::Literal::Group(A::commit_to_group_bhp768(&input.to_bits_le(), &randomizer)),
185 3 => circuit::Literal::Group(A::commit_to_group_bhp1024(&input.to_bits_le(), &randomizer)),
186 4 => circuit::Literal::Group(A::commit_to_group_ped64(&input.to_bits_le(), &randomizer)),
187 5 => circuit::Literal::Group(A::commit_to_group_ped128(&input.to_bits_le(), &randomizer)),
188 6.. => bail!("Invalid 'commit' variant: {VARIANT}"),
189 };
190 let output = output.cast_lossy(self.destination_type)?;
191 let output = circuit::Value::Plaintext(circuit::Plaintext::Literal(output, Default::default()));
193 registers.store_circuit(stack, &self.destination, output)
195 }
196
197 #[inline]
199 pub fn finalize(
200 &self,
201 stack: &(impl StackMatches<N> + StackProgram<N>),
202 registers: &mut (impl RegistersLoad<N> + RegistersStore<N>),
203 ) -> Result<()> {
204 self.evaluate(stack, registers)
205 }
206
207 #[inline]
209 pub fn output_types(
210 &self,
211 _stack: &impl StackProgram<N>,
212 input_types: &[RegisterType<N>],
213 ) -> Result<Vec<RegisterType<N>>> {
214 if input_types.len() != 2 {
216 bail!("Instruction '{}' expects 2 inputs, found {} inputs", Self::opcode(), input_types.len())
217 }
218 if self.operands.len() != 2 {
220 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
221 }
222 ensure!(is_valid_destination_type(self.destination_type), "Invalid destination type in 'commit' instruction");
224
225 match VARIANT {
228 0..=5 => Ok(vec![RegisterType::Plaintext(PlaintextType::Literal(self.destination_type))]),
229 6.. => bail!("Invalid 'commit' variant: {VARIANT}"),
230 }
231 }
232}
233
234impl<N: Network, const VARIANT: u8> Parser for CommitInstruction<N, VARIANT> {
235 #[inline]
237 fn parse(string: &str) -> ParserResult<Self> {
238 let (string, _) = tag(*Self::opcode())(string)?;
240 let (string, _) = Sanitizer::parse_whitespaces(string)?;
242 let (string, first) = Operand::parse(string)?;
244 let (string, _) = Sanitizer::parse_whitespaces(string)?;
246 let (string, second) = Operand::parse(string)?;
248 let (string, _) = Sanitizer::parse_whitespaces(string)?;
250 let (string, _) = tag("into")(string)?;
252 let (string, _) = Sanitizer::parse_whitespaces(string)?;
254 let (string, destination) = Register::parse(string)?;
256 let (string, _) = Sanitizer::parse_whitespaces(string)?;
258 let (string, _) = tag("as")(string)?;
260 let (string, _) = Sanitizer::parse_whitespaces(string)?;
262 let (string, destination_type) = LiteralType::parse(string)?;
264 match destination_type {
266 LiteralType::Address | LiteralType::Field | LiteralType::Group => {
267 Ok((string, Self { operands: vec![first, second], destination, destination_type }))
268 }
269 _ => map_res(fail, |_: ParserResult<Self>| {
270 Err(error(format!("Failed to parse 'commit': '{destination_type}' is invalid")))
271 })(string),
272 }
273 }
274}
275
276impl<N: Network, const VARIANT: u8> FromStr for CommitInstruction<N, VARIANT> {
277 type Err = Error;
278
279 #[inline]
281 fn from_str(string: &str) -> Result<Self> {
282 match Self::parse(string) {
283 Ok((remainder, object)) => {
284 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
286 Ok(object)
288 }
289 Err(error) => bail!("Failed to parse string. {error}"),
290 }
291 }
292}
293
294impl<N: Network, const VARIANT: u8> Debug for CommitInstruction<N, VARIANT> {
295 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
297 Display::fmt(self, f)
298 }
299}
300
301impl<N: Network, const VARIANT: u8> Display for CommitInstruction<N, VARIANT> {
302 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
304 if self.operands.len() != 2 {
306 return Err(fmt::Error);
307 }
308 write!(f, "{} ", Self::opcode())?;
310 self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
311 write!(f, "into {} as {}", self.destination, self.destination_type)
312 }
313}
314
315impl<N: Network, const VARIANT: u8> FromBytes for CommitInstruction<N, VARIANT> {
316 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
318 let mut operands = Vec::with_capacity(2);
320 for _ in 0..2 {
322 operands.push(Operand::read_le(&mut reader)?);
323 }
324 let destination = Register::read_le(&mut reader)?;
326 let destination_type = LiteralType::read_le(&mut reader)?;
328
329 Self::new(operands, destination, destination_type).map_err(error)
331 }
332}
333
334impl<N: Network, const VARIANT: u8> ToBytes for CommitInstruction<N, VARIANT> {
335 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
337 if self.operands.len() != 2 {
339 return Err(error(format!("The number of operands must be 2, found {}", self.operands.len())));
340 }
341 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
343 self.destination.write_le(&mut writer)?;
345 self.destination_type.write_le(&mut writer)
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353 use console::network::MainnetV0;
354
355 type CurrentNetwork = MainnetV0;
356
357 fn valid_destination_types() -> &'static [LiteralType] {
359 &[LiteralType::Address, LiteralType::Field, LiteralType::Group]
360 }
361
362 #[test]
363 fn test_parse() {
364 for destination_type in valid_destination_types() {
365 let instruction = format!("commit.bhp512 r0 r1 into r2 as {destination_type}");
366 let (string, commit) = CommitBHP512::<CurrentNetwork>::parse(&instruction).unwrap();
367 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
368 assert_eq!(commit.operands.len(), 2, "The number of operands is incorrect");
369 assert_eq!(commit.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
370 assert_eq!(commit.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
371 assert_eq!(commit.destination, Register::Locator(2), "The destination register is incorrect");
372 assert_eq!(commit.destination_type, *destination_type, "The destination type is incorrect");
373 }
374 }
375}