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
251#[cfg(test)]
252pub(crate) mod test_helpers {
253 use super::*;
254 use console::{network::MainnetV0, program::Literal};
255
256 type CurrentNetwork = MainnetV0;
257
258 pub(crate) fn sample_inputs() -> Vec<(<CurrentNetwork as Network>::TransitionID, Input<CurrentNetwork>)> {
260 let rng = &mut TestRng::default();
261
262 let transaction = crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0);
264 let transition = transaction.transitions().next().unwrap();
265
266 let transition_id = *transition.id();
268 let input = transition.inputs().iter().next().unwrap().clone();
269
270 let plaintext = Plaintext::Literal(Literal::Field(Uniform::rand(rng)), Default::default());
272 let plaintext_hash = CurrentNetwork::hash_bhp1024(&plaintext.to_bits_le()).unwrap();
273 let fields: Vec<_> = (0..10).map(|_| Uniform::rand(rng)).collect();
275 let ciphertext = Ciphertext::from_fields(&fields).unwrap();
276 let ciphertext_hash = CurrentNetwork::hash_bhp1024(&ciphertext.to_bits_le()).unwrap();
277
278 vec![
279 (transition_id, input),
280 (Uniform::rand(rng), Input::Constant(Uniform::rand(rng), None)),
281 (Uniform::rand(rng), Input::Constant(plaintext_hash, Some(plaintext.clone()))),
282 (Uniform::rand(rng), Input::Public(Uniform::rand(rng), None)),
283 (Uniform::rand(rng), Input::Public(plaintext_hash, Some(plaintext))),
284 (Uniform::rand(rng), Input::Private(Uniform::rand(rng), None)),
285 (Uniform::rand(rng), Input::Private(ciphertext_hash, Some(ciphertext))),
286 (Uniform::rand(rng), Input::Record(Uniform::rand(rng), Uniform::rand(rng))),
287 (Uniform::rand(rng), Input::ExternalRecord(Uniform::rand(rng))),
288 (
289 Uniform::rand(rng),
290 Input::RecordWithDynamicID(Uniform::rand(rng), Uniform::rand(rng), Uniform::rand(rng)),
291 ),
292 (Uniform::rand(rng), Input::ExternalRecordWithDynamicID(Uniform::rand(rng), Uniform::rand(rng))),
293 (Uniform::rand(rng), Input::DynamicRecord(Uniform::rand(rng))),
294 ]
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use console::network::MainnetV0;
302
303 type CurrentNetwork = MainnetV0;
304
305 #[test]
306 fn test_to_caller_input_record_with_dynamic_id() {
307 let serial_number = Field::<CurrentNetwork>::from_u64(1);
309 let tag = Field::<CurrentNetwork>::from_u64(2);
310 let dynamic_id = Field::<CurrentNetwork>::from_u64(3);
311
312 let input = Input::<CurrentNetwork>::RecordWithDynamicID(serial_number, tag, dynamic_id);
313 let caller_input = input.to_caller_input();
314
315 assert_eq!(caller_input, Input::<CurrentNetwork>::DynamicRecord(dynamic_id));
316 }
317
318 #[test]
319 fn test_to_caller_input_external_record_with_dynamic_id() {
320 let ext_id = Field::<CurrentNetwork>::from_u64(10);
322 let dynamic_id = Field::<CurrentNetwork>::from_u64(20);
323
324 let input = Input::<CurrentNetwork>::ExternalRecordWithDynamicID(ext_id, dynamic_id);
325 let caller_input = input.to_caller_input();
326
327 assert_eq!(caller_input, Input::<CurrentNetwork>::DynamicRecord(dynamic_id));
328 }
329
330 #[test]
331 fn test_to_caller_input_non_dynamic_variants_unchanged() {
332 let id = Field::<CurrentNetwork>::from_u64(42);
334
335 let constant = Input::<CurrentNetwork>::Constant(id, None);
336 assert_eq!(constant.to_caller_input(), constant);
337
338 let public = Input::<CurrentNetwork>::Public(id, None);
339 assert_eq!(public.to_caller_input(), public);
340
341 let dynamic_record = Input::<CurrentNetwork>::DynamicRecord(id);
342 assert_eq!(dynamic_record.to_caller_input(), dynamic_record);
343
344 let external = Input::<CurrentNetwork>::ExternalRecord(id);
345 assert_eq!(external.to_caller_input(), external);
346 }
347}