snarkvm_synthesizer_program/logic/instruction/operation/
sign_verify.rsuse crate::{
Opcode,
Operand,
traits::{RegistersLoad, RegistersLoadCircuit, RegistersStore, RegistersStoreCircuit, StackMatches, StackProgram},
};
use circuit::prelude::ToFields as CircuitToFields;
use console::{
network::prelude::*,
program::{Literal, LiteralType, PlaintextType, Register, RegisterType, ToFields as ConsoleToFields},
types::Boolean,
};
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct SignVerify<N: Network> {
operands: Vec<Operand<N>>,
destination: Register<N>,
}
impl<N: Network> SignVerify<N> {
#[inline]
pub fn new(operands: Vec<Operand<N>>, destination: Register<N>) -> Result<Self> {
ensure!(operands.len() == 3, "Instruction '{}' must have three operands", Self::opcode());
Ok(Self { operands, destination })
}
#[inline]
pub const fn opcode() -> Opcode {
Opcode::Sign
}
#[inline]
pub fn operands(&self) -> &[Operand<N>] {
debug_assert!(self.operands.len() == 3, "Instruction '{}' must have three operands", Self::opcode());
&self.operands
}
#[inline]
pub fn destinations(&self) -> Vec<Register<N>> {
vec![self.destination.clone()]
}
}
impl<N: Network> SignVerify<N> {
#[inline]
pub fn evaluate(
&self,
stack: &(impl StackMatches<N> + StackProgram<N>),
registers: &mut (impl RegistersLoad<N> + RegistersStore<N>),
) -> Result<()> {
if self.operands.len() != 3 {
bail!("Instruction '{}' expects 3 operands, found {} operands", Self::opcode(), self.operands.len())
}
let signature = match registers.load_literal(stack, &self.operands[0])? {
Literal::Signature(signature) => signature,
_ => bail!("Expected the first operand to be a signature."),
};
let address = match registers.load_literal(stack, &self.operands[1])? {
Literal::Address(address) => address,
_ => bail!("Expected the second operand to be an address."),
};
let message = registers.load(stack, &self.operands[2])?;
let output = Literal::Boolean(Boolean::new(signature.verify(&address, &message.to_fields()?)));
registers.store_literal(stack, &self.destination, output)
}
#[inline]
pub fn execute<A: circuit::Aleo<Network = N>>(
&self,
stack: &(impl StackMatches<N> + StackProgram<N>),
registers: &mut (impl RegistersLoadCircuit<N, A> + RegistersStoreCircuit<N, A>),
) -> Result<()> {
if self.operands.len() != 3 {
bail!("Instruction '{}' expects 3 operands, found {} operands", Self::opcode(), self.operands.len())
}
let signature = match registers.load_literal_circuit(stack, &self.operands[0])? {
circuit::Literal::Signature(signature) => signature,
_ => bail!("Expected the first operand to be a signature."),
};
let address = match registers.load_literal_circuit(stack, &self.operands[1])? {
circuit::Literal::Address(address) => address,
_ => bail!("Expected the second operand to be an address."),
};
let message = registers.load_circuit(stack, &self.operands[2])?;
let output = circuit::Literal::Boolean(signature.verify(&address, &message.to_fields()));
registers.store_literal_circuit(stack, &self.destination, output)
}
#[inline]
pub fn finalize(
&self,
stack: &(impl StackMatches<N> + StackProgram<N>),
registers: &mut (impl RegistersLoad<N> + RegistersStore<N>),
) -> Result<()> {
self.evaluate(stack, registers)
}
#[inline]
pub fn output_types(
&self,
_stack: &impl StackProgram<N>,
input_types: &[RegisterType<N>],
) -> Result<Vec<RegisterType<N>>> {
if input_types.len() != 3 {
bail!("Instruction '{}' expects 3 inputs, found {} inputs", Self::opcode(), input_types.len())
}
if input_types[0] != RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Signature)) {
bail!(
"Instruction '{}' expects the first input to be a 'signature'. Found input of type '{}'",
Self::opcode(),
input_types[0]
)
}
if input_types[1] != RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Address)) {
bail!(
"Instruction '{}' expects the second input to be an 'address'. Found input of type '{}'",
Self::opcode(),
input_types[1]
)
}
Ok(vec![RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Boolean))])
}
}
impl<N: Network> Parser for SignVerify<N> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
let (string, _) = tag(*Self::opcode())(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, first) = Operand::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, second) = Operand::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, third) = Operand::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag("into")(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, destination) = Register::parse(string)?;
Ok((string, Self { operands: vec![first, second, third], destination }))
}
}
impl<N: Network> FromStr for SignVerify<N> {
type Err = Error;
#[inline]
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
impl<N: Network> Debug for SignVerify<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network> Display for SignVerify<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.operands.len() != 3 {
return Err(fmt::Error);
}
write!(f, "{} ", Self::opcode())?;
self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
write!(f, "into {}", self.destination)
}
}
impl<N: Network> FromBytes for SignVerify<N> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let mut operands = Vec::with_capacity(3);
for _ in 0..3 {
operands.push(Operand::read_le(&mut reader)?);
}
let destination = Register::read_le(&mut reader)?;
Ok(Self { operands, destination })
}
}
impl<N: Network> ToBytes for SignVerify<N> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
if self.operands.len() != 3 {
return Err(error(format!("The number of operands must be 3, found {}", self.operands.len())));
}
self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
self.destination.write_le(&mut writer)
}
}
#[cfg(test)]
mod tests {
use super::*;
use console::network::MainnetV0;
type CurrentNetwork = MainnetV0;
#[test]
fn test_parse() {
let (string, is) = SignVerify::<CurrentNetwork>::parse("sign.verify r0 r1 r2 into r3").unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
assert_eq!(is.operands.len(), 3, "The number of operands is incorrect");
assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
assert_eq!(is.operands[2], Operand::Register(Register::Locator(2)), "The third operand is incorrect");
assert_eq!(is.destination, Register::Locator(3), "The destination register is incorrect");
}
}