use crate::{Identifier, ProgramID, Register, Value, ValueType, compute_function_id};
use snarkvm_console_network::Network;
use snarkvm_console_types::prelude::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OutputID<N: Network> {
Constant(Field<N>),
Public(Field<N>),
Private(Field<N>),
Record(Field<N>, Field<N>),
ExternalRecord(Field<N>),
Future(Field<N>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Response<N: Network> {
output_ids: Vec<OutputID<N>>,
outputs: Vec<Value<N>>,
}
impl<N: Network> From<(Vec<OutputID<N>>, Vec<Value<N>>)> for Response<N> {
fn from((output_ids, outputs): (Vec<OutputID<N>>, Vec<Value<N>>)) -> Self {
Self { output_ids, outputs }
}
}
impl<N: Network> Response<N> {
pub fn new(
network_id: &U16<N>,
program_id: &ProgramID<N>,
function_name: &Identifier<N>,
num_inputs: usize,
tvk: &Field<N>,
tcm: &Field<N>,
outputs: Vec<Value<N>>,
output_types: &[ValueType<N>],
output_operands: &[Option<Register<N>>],
) -> Result<Self> {
let function_id = compute_function_id(network_id, program_id, function_name)?;
let output_ids = outputs
.iter()
.zip_eq(output_types)
.zip_eq(output_operands)
.enumerate()
.map(|(index, ((output, output_type), output_register))| {
match output_type {
ValueType::Constant(..) => {
ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
let index = Field::from_u16(
u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
);
let mut preimage = Vec::new();
preimage.push(function_id);
preimage.extend(output.to_fields()?);
preimage.push(*tcm);
preimage.push(index);
let output_hash = N::hash_psd8(&preimage)?;
Ok(OutputID::Constant(output_hash))
}
ValueType::Public(..) => {
ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
let index = Field::from_u16(
u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
);
let mut preimage = Vec::new();
preimage.push(function_id);
preimage.extend(output.to_fields()?);
preimage.push(*tcm);
preimage.push(index);
let output_hash = N::hash_psd8(&preimage)?;
Ok(OutputID::Public(output_hash))
}
ValueType::Private(..) => {
ensure!(matches!(output, Value::Plaintext(..)), "Expected a plaintext output");
let index = Field::from_u16(
u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
);
let output_view_key = N::hash_psd4(&[function_id, *tvk, index])?;
let ciphertext = match &output {
Value::Plaintext(plaintext) => plaintext.encrypt_symmetric(output_view_key)?,
Value::Record(..) => bail!("Expected a plaintext output, found a record output"),
Value::Future(..) => bail!("Expected a plaintext output, found a future output"),
};
let output_hash = N::hash_psd8(&ciphertext.to_fields()?)?;
Ok(OutputID::Private(output_hash))
}
ValueType::Record(record_name) => {
let record = match &output {
Value::Record(record) => record,
Value::Plaintext(..) => bail!("Expected a record output, found a plaintext output"),
Value::Future(..) => bail!("Expected a record output, found a future output"),
};
let output_register = match output_register {
Some(output_register) => output_register,
None => bail!("Expected a register to be paired with a record output"),
};
let commitment = record.to_commitment(program_id, record_name)?;
let index = Field::from_u64(output_register.locator());
let randomizer = N::hash_to_scalar_psd2(&[*tvk, index])?;
let encrypted_record = record.encrypt(randomizer)?;
let checksum = N::hash_bhp1024(&encrypted_record.to_bits_le())?;
Ok(OutputID::Record(commitment, checksum))
}
ValueType::ExternalRecord(..) => {
ensure!(matches!(output, Value::Record(..)), "Expected a record output");
let index = Field::from_u16(
u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
);
let mut preimage = Vec::new();
preimage.push(function_id);
preimage.extend(output.to_fields()?);
preimage.push(*tvk);
preimage.push(index);
let output_hash = N::hash_psd8(&preimage)?;
Ok(OutputID::ExternalRecord(output_hash))
}
ValueType::Future(..) => {
ensure!(matches!(output, Value::Future(..)), "Expected a future output");
let index = Field::from_u16(
u16::try_from(num_inputs + index).or_halt_with::<N>("Output index exceeds u16"),
);
let mut preimage = Vec::new();
preimage.push(function_id);
preimage.extend(output.to_fields()?);
preimage.push(*tcm);
preimage.push(index);
let output_hash = N::hash_psd8(&preimage)?;
Ok(OutputID::Future(output_hash))
}
}
})
.collect::<Result<Vec<_>>>()?;
Ok(Self { output_ids, outputs })
}
pub fn output_ids(&self) -> &[OutputID<N>] {
&self.output_ids
}
pub fn outputs(&self) -> &[Value<N>] {
&self.outputs
}
}