mod input_id;
pub use input_id::InputID;
mod bytes;
mod serialize;
mod sign;
mod string;
mod verify;
use crate::{DynamicRecord, Identifier, Plaintext, ProgramID, Record, Value, ValueType, compute_function_id};
use snarkvm_console_account::{Address, ComputeKey, GraphKey, PrivateKey, Signature, ViewKey};
use snarkvm_console_network::Network;
use snarkvm_console_types::prelude::*;
#[derive(Clone, PartialEq, Eq)]
pub struct Request<N: Network> {
signer: Address<N>,
network_id: U16<N>,
program_id: ProgramID<N>,
function_name: Identifier<N>,
input_ids: Vec<InputID<N>>,
inputs: Vec<Value<N>>,
signature: Signature<N>,
sk_tag: Field<N>,
tvk: Field<N>,
tcm: Field<N>,
scm: Field<N>,
is_dynamic: bool,
}
impl<N: Network>
From<(
Address<N>,
U16<N>,
ProgramID<N>,
Identifier<N>,
Vec<InputID<N>>,
Vec<Value<N>>,
Signature<N>,
Field<N>,
Field<N>,
Field<N>,
Field<N>,
bool,
)> for Request<N>
{
fn from(
(
signer,
network_id,
program_id,
function_name,
input_ids,
inputs,
signature,
sk_tag,
tvk,
tcm,
scm,
is_dynamic,
): (
Address<N>,
U16<N>,
ProgramID<N>,
Identifier<N>,
Vec<InputID<N>>,
Vec<Value<N>>,
Signature<N>,
Field<N>,
Field<N>,
Field<N>,
Field<N>,
bool,
),
) -> Self {
if inputs.len() != input_ids.len() {
N::halt(format!(
"Invalid request: mismatching number of input IDs ({}) and inputs ({})",
input_ids.len(),
inputs.len()
))
}
if *network_id != N::ID {
N::halt(format!("Invalid network ID. Expected {}, found {}", N::ID, *network_id))
} else {
Self {
signer,
network_id,
program_id,
function_name,
input_ids,
inputs,
signature,
sk_tag,
tvk,
tcm,
scm,
is_dynamic,
}
}
}
}
impl<N: Network> Request<N> {
pub const fn signer(&self) -> &Address<N> {
&self.signer
}
pub const fn network_id(&self) -> &U16<N> {
&self.network_id
}
pub const fn program_id(&self) -> &ProgramID<N> {
&self.program_id
}
pub const fn function_name(&self) -> &Identifier<N> {
&self.function_name
}
pub fn input_ids(&self) -> &[InputID<N>] {
&self.input_ids
}
pub fn inputs(&self) -> &[Value<N>] {
&self.inputs
}
pub const fn signature(&self) -> &Signature<N> {
&self.signature
}
pub const fn sk_tag(&self) -> &Field<N> {
&self.sk_tag
}
pub const fn tvk(&self) -> &Field<N> {
&self.tvk
}
pub fn to_tpk(&self) -> Group<N> {
let challenge = self.signature.challenge();
let response = self.signature.response();
let pk_sig = self.signature.compute_key().pk_sig();
(pk_sig * challenge) + N::g_scalar_multiply(&response)
}
pub const fn tcm(&self) -> &Field<N> {
&self.tcm
}
pub const fn scm(&self) -> &Field<N> {
&self.scm
}
pub const fn is_dynamic(&self) -> bool {
self.is_dynamic
}
pub fn to_dynamic_input_ids(&self) -> Result<Vec<InputID<N>>> {
let function_id = compute_function_id(&self.network_id, &self.program_id, &self.function_name)?;
ensure!(
self.input_ids().len() == self.inputs.len(),
"Mismatched number of input IDs and inputs: {} vs. {}",
self.input_ids().len(),
self.inputs.len(),
);
self.input_ids()
.iter()
.zip(self.inputs.iter())
.enumerate()
.map(|(index, (input_id, input))| match (input_id, input) {
(InputID::Constant(..), Value::Plaintext(..))
| (InputID::Public(..), Value::Plaintext(..))
| (InputID::Private(..), Value::Plaintext(..))
| (InputID::DynamicRecord(..), Value::DynamicRecord(..)) => Ok(*input_id),
(InputID::Record(..), Value::Record(record)) | (InputID::ExternalRecord(..), Value::Record(record)) => {
let index = u16::try_from(index).map_err(|_| anyhow!("Input index exceeds u16"))?;
let caller_input = Value::DynamicRecord(DynamicRecord::from_record(record)?);
InputID::dynamic_record(function_id, &caller_input, self.tvk, index)
}
_ => bail!("Mismatching input ID and input value at index {index}"),
})
.collect()
}
pub fn to_dynamic_inputs(&self) -> Result<Vec<Value<N>>> {
self.inputs
.iter()
.map(|input| match input {
Value::Record(record) => {
Ok(Value::DynamicRecord(DynamicRecord::from_record(record)?))
}
Value::Future(_) => bail!("A future cannot be an input to a request."),
Value::DynamicFuture(_) => bail!("A dynamic future cannot be an input to a request."),
_ => Ok(input.clone()),
})
.collect::<Result<Vec<_>>>()
}
}
#[cfg(test)]
mod test_helpers {
use super::*;
use snarkvm_console_network::MainnetV0;
type CurrentNetwork = MainnetV0;
const ITERATIONS: u64 = 1000;
pub(super) fn sample_requests(rng: &mut TestRng) -> Vec<Request<CurrentNetwork>> {
(0..ITERATIONS)
.map(|i| {
let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let address = Address::try_from(&private_key).unwrap();
let program_id = ProgramID::from_str("token.aleo").unwrap();
let function_name = Identifier::from_str("transfer").unwrap();
let record_string =
format!("{{ owner: {address}.private, token_amount: {i}u64.private, _nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public }}");
let input_constant = Value::from_str(&format!("{{ token_amount: {i}u128 }}")).unwrap();
let input_public = Value::from_str(&format!("{{ token_amount: {i}u128 }}")).unwrap();
let input_private = Value::from_str(&format!("{{ token_amount: {i}u128 }}")).unwrap();
let input_record = Value::from_str(&record_string).unwrap();
let input_external_record = Value::from_str(&record_string).unwrap();
let inputs = vec![input_constant, input_public, input_private, input_record, input_external_record];
let input_types = [
ValueType::from_str("amount.constant").unwrap(),
ValueType::from_str("amount.public").unwrap(),
ValueType::from_str("amount.private").unwrap(),
ValueType::from_str("token.record").unwrap(),
ValueType::from_str("token.aleo/token.record").unwrap(),
];
let root_tvk = None;
let is_root = Uniform::rand(rng);
let program_checksum = match bool::rand(rng) {
true => Some(Field::rand(rng)),
false => None,
};
let is_dynamic = bool::rand(rng);
let request = Request::sign(
&private_key,
program_id,
function_name,
inputs.into_iter(),
&input_types,
root_tvk,
is_root,
program_checksum,
is_dynamic,
rng,
)
.unwrap();
assert!(request.verify(&input_types, is_root, program_checksum));
request
})
.collect()
}
}