1mod bytes;
17mod serialize;
18mod string;
19
20use console::{
21 network::prelude::*,
22 program::{Ciphertext, Plaintext, TransitionLeaf, ValueType},
23 types::Field,
24};
25
26type Variant = u8;
27
28#[derive(Clone, PartialEq, Eq)]
30pub enum Input<N: Network> {
31 Constant(Field<N>, Option<Plaintext<N>>),
33 Public(Field<N>, Option<Plaintext<N>>),
35 Private(Field<N>, Option<Ciphertext<N>>),
37 Record(Field<N>, Field<N>),
39 ExternalRecord(Field<N>),
41 DynamicRecord(Field<N>),
43 RecordWithDynamicID(Field<N>, Field<N>, Field<N>),
47 ExternalRecordWithDynamicID(Field<N>, Field<N>),
51}
52
53impl<N: Network> Input<N> {
54 pub const fn variant(&self) -> Variant {
56 match self {
57 Input::Constant(..) => 0,
58 Input::Public(..) => 1,
59 Input::Private(..) => 2,
60 Input::Record(..) => 3, Input::ExternalRecord(..) => 4,
62 Input::DynamicRecord(..) => 5,
63 Input::RecordWithDynamicID(..) => 6,
64 Input::ExternalRecordWithDynamicID(..) => 7,
65 }
66 }
67
68 pub const fn id(&self) -> &Field<N> {
70 match self {
71 Input::Constant(id, ..) => id,
72 Input::Public(id, ..) => id,
73 Input::Private(id, ..) => id,
74 Input::Record(serial_number, ..) => serial_number,
75 Input::ExternalRecord(id) => id,
76 Input::DynamicRecord(id) => id,
77 Input::RecordWithDynamicID(serial_number, ..) => serial_number,
78 Input::ExternalRecordWithDynamicID(id, ..) => id,
79 }
80 }
81
82 pub fn to_transition_leaf(&self, index: u8) -> TransitionLeaf<N> {
86 match self {
87 Input::RecordWithDynamicID(..) => TransitionLeaf::new_record_with_dynamic_id(index, *self.id()),
89 Input::ExternalRecordWithDynamicID(..) => {
91 TransitionLeaf::new_external_record_with_dynamic_id(index, *self.id())
92 }
93 _ => TransitionLeaf::new(index, self.variant(), *self.id()),
95 }
96 }
97
98 pub const fn tag(&self) -> Option<&Field<N>> {
100 match self {
101 Input::Record(_, tag) | Input::RecordWithDynamicID(_, tag, _) => Some(tag),
102 _ => None,
103 }
104 }
105
106 pub fn into_tag(self) -> Option<Field<N>> {
108 match self {
109 Input::Record(_, tag) | Input::RecordWithDynamicID(_, tag, _) => Some(tag),
110 _ => None,
111 }
112 }
113
114 pub const fn serial_number(&self) -> Option<&Field<N>> {
116 match self {
117 Input::Record(serial_number, ..) | Input::RecordWithDynamicID(serial_number, ..) => Some(serial_number),
118 _ => None,
119 }
120 }
121
122 pub fn into_serial_number(self) -> Option<Field<N>> {
124 match self {
125 Input::Record(serial_number, ..) | Input::RecordWithDynamicID(serial_number, ..) => Some(serial_number),
126 _ => None,
127 }
128 }
129
130 pub fn verifier_inputs(&self) -> impl '_ + Iterator<Item = N::Field> {
132 [Some(self.id()), self.tag()].into_iter().flatten().map(|id| **id)
133 }
134
135 pub const fn dynamic_id(&self) -> Option<&Field<N>> {
137 match self {
138 Input::RecordWithDynamicID(_, _, dynamic_id) | Input::ExternalRecordWithDynamicID(_, dynamic_id) => {
139 Some(dynamic_id)
140 }
141 _ => None,
142 }
143 }
144
145 pub fn to_caller_input(&self) -> Self {
149 match self {
150 Self::RecordWithDynamicID(_, _, dynamic_id) => Self::DynamicRecord(*dynamic_id),
152 Self::ExternalRecordWithDynamicID(_, dynamic_id) => Self::DynamicRecord(*dynamic_id),
154 other => other.clone(),
156 }
157 }
158
159 pub fn verify(&self, function_id: Field<N>, tcm: &Field<N>, index: usize) -> bool {
162 let result = || match self {
164 Input::Constant(hash, Some(input)) => {
165 match input.to_fields() {
166 Ok(fields) => {
167 let index = Field::from_u16(index as u16);
169 let mut preimage = Vec::new();
171 preimage.push(function_id);
172 preimage.extend(fields);
173 preimage.push(*tcm);
174 preimage.push(index);
175 match N::hash_psd8(&preimage) {
177 Ok(candidate_hash) => Ok(hash == &candidate_hash),
178 Err(error) => Err(error),
179 }
180 }
181 Err(error) => Err(error),
182 }
183 }
184 Input::Public(hash, Some(input)) => {
185 match input.to_fields() {
186 Ok(fields) => {
187 let index = Field::from_u16(index as u16);
189 let mut preimage = Vec::new();
191 preimage.push(function_id);
192 preimage.extend(fields);
193 preimage.push(*tcm);
194 preimage.push(index);
195 match N::hash_psd8(&preimage) {
197 Ok(candidate_hash) => Ok(hash == &candidate_hash),
198 Err(error) => Err(error),
199 }
200 }
201 Err(error) => Err(error),
202 }
203 }
204 Input::Private(hash, Some(value)) => {
205 match value.to_fields() {
206 Ok(fields) => match N::hash_psd8(&fields) {
208 Ok(candidate_hash) => Ok(hash == &candidate_hash),
209 Err(error) => Err(error),
210 },
211 Err(error) => Err(error),
212 }
213 }
214 Input::Constant(_, None) | Input::Public(_, None) | Input::Private(_, None) => {
215 bail!("A transition input value is missing")
218 }
219 Input::Record(_, _)
220 | Input::ExternalRecord(_)
221 | Input::DynamicRecord(_)
222 | Input::RecordWithDynamicID(_, _, _)
223 | Input::ExternalRecordWithDynamicID(_, _) => Ok(true),
224 };
225
226 match result() {
227 Ok(is_hash_valid) => is_hash_valid,
228 Err(error) => {
229 eprintln!("{error}");
230 false
231 }
232 }
233 }
234
235 pub fn is_type(&self, expected_value_type: &ValueType<N>) -> bool {
237 matches!(
238 (self, expected_value_type),
239 (Self::Constant(..), ValueType::Constant(..))
240 | (Self::Public(..), ValueType::Public(..))
241 | (Self::Private(..), ValueType::Private(..))
242 | (Self::Record(..), ValueType::Record(..))
243 | (Self::RecordWithDynamicID(..), ValueType::Record(..))
244 | (Self::ExternalRecord(..), ValueType::ExternalRecord(..))
245 | (Self::ExternalRecordWithDynamicID(..), ValueType::ExternalRecord(..))
246 | (Self::DynamicRecord(..), ValueType::DynamicRecord)
247 )
248 }
249
250 pub fn valid_as_argument(&self, callee_value_type: &ValueType<N>, is_dynamic: Option<bool>) -> bool {
261 if matches!(
262 (self, callee_value_type),
263 (Self::Constant(..), ValueType::Constant(..))
264 | (Self::Public(..), ValueType::Public(..))
265 | (Self::Private(..), ValueType::Private(..))
266 ) {
267 return true;
268 }
269
270 if is_dynamic == Some(true) {
271 matches!(
276 (self, callee_value_type),
277 (Self::RecordWithDynamicID(..), ValueType::Record(..))
279 | (Self::ExternalRecordWithDynamicID(..), ValueType::ExternalRecord(..))
281 | (Self::DynamicRecord(..), ValueType::DynamicRecord)
283 )
284 } else if is_dynamic == Some(false) {
285 matches!(
289 (self, callee_value_type),
290 (Self::Record(..), ValueType::Record(..))
293 | (Self::ExternalRecord(..), ValueType::ExternalRecord(..))
295 | (Self::DynamicRecord(..), ValueType::DynamicRecord)
297 )
298 } else {
299 self.is_type(callee_value_type)
301 }
302 }
303}
304
305#[cfg(test)]
306pub(crate) mod test_helpers {
307 use super::*;
308 use console::{network::MainnetV0, program::Literal};
309
310 type CurrentNetwork = MainnetV0;
311
312 pub(crate) fn sample_inputs() -> Vec<(<CurrentNetwork as Network>::TransitionID, Input<CurrentNetwork>)> {
314 let rng = &mut TestRng::default();
315
316 let transaction = crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0);
318 let transition = transaction.transitions().next().unwrap();
319
320 let transition_id = *transition.id();
322 let input = transition.inputs().iter().next().unwrap().clone();
323
324 let plaintext = Plaintext::Literal(Literal::Field(Uniform::rand(rng)), Default::default());
326 let plaintext_hash = CurrentNetwork::hash_bhp1024(&plaintext.to_bits_le()).unwrap();
327 let fields: Vec<_> = (0..10).map(|_| Uniform::rand(rng)).collect();
329 let ciphertext = Ciphertext::from_fields(&fields).unwrap();
330 let ciphertext_hash = CurrentNetwork::hash_bhp1024(&ciphertext.to_bits_le()).unwrap();
331
332 vec![
333 (transition_id, input),
334 (Uniform::rand(rng), Input::Constant(Uniform::rand(rng), None)),
335 (Uniform::rand(rng), Input::Constant(plaintext_hash, Some(plaintext.clone()))),
336 (Uniform::rand(rng), Input::Public(Uniform::rand(rng), None)),
337 (Uniform::rand(rng), Input::Public(plaintext_hash, Some(plaintext))),
338 (Uniform::rand(rng), Input::Private(Uniform::rand(rng), None)),
339 (Uniform::rand(rng), Input::Private(ciphertext_hash, Some(ciphertext))),
340 (Uniform::rand(rng), Input::Record(Uniform::rand(rng), Uniform::rand(rng))),
341 (Uniform::rand(rng), Input::ExternalRecord(Uniform::rand(rng))),
342 (
343 Uniform::rand(rng),
344 Input::RecordWithDynamicID(Uniform::rand(rng), Uniform::rand(rng), Uniform::rand(rng)),
345 ),
346 (Uniform::rand(rng), Input::ExternalRecordWithDynamicID(Uniform::rand(rng), Uniform::rand(rng))),
347 (Uniform::rand(rng), Input::DynamicRecord(Uniform::rand(rng))),
348 ]
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355 use console::network::MainnetV0;
356
357 type CurrentNetwork = MainnetV0;
358
359 #[test]
360 fn test_to_caller_input_record_with_dynamic_id() {
361 let serial_number = Field::<CurrentNetwork>::from_u64(1);
363 let tag = Field::<CurrentNetwork>::from_u64(2);
364 let dynamic_id = Field::<CurrentNetwork>::from_u64(3);
365
366 let input = Input::<CurrentNetwork>::RecordWithDynamicID(serial_number, tag, dynamic_id);
367 let caller_input = input.to_caller_input();
368
369 assert_eq!(caller_input, Input::<CurrentNetwork>::DynamicRecord(dynamic_id));
370 }
371
372 #[test]
373 fn test_to_caller_input_external_record_with_dynamic_id() {
374 let ext_id = Field::<CurrentNetwork>::from_u64(10);
376 let dynamic_id = Field::<CurrentNetwork>::from_u64(20);
377
378 let input = Input::<CurrentNetwork>::ExternalRecordWithDynamicID(ext_id, dynamic_id);
379 let caller_input = input.to_caller_input();
380
381 assert_eq!(caller_input, Input::<CurrentNetwork>::DynamicRecord(dynamic_id));
382 }
383
384 #[test]
385 fn test_to_caller_input_non_dynamic_variants_unchanged() {
386 let id = Field::<CurrentNetwork>::from_u64(42);
388
389 let constant = Input::<CurrentNetwork>::Constant(id, None);
390 assert_eq!(constant.to_caller_input(), constant);
391
392 let public = Input::<CurrentNetwork>::Public(id, None);
393 assert_eq!(public.to_caller_input(), public);
394
395 let dynamic_record = Input::<CurrentNetwork>::DynamicRecord(id);
396 assert_eq!(dynamic_record.to_caller_input(), dynamic_record);
397
398 let external = Input::<CurrentNetwork>::ExternalRecord(id);
399 assert_eq!(external.to_caller_input(), external);
400 }
401}