snarkvm_ledger_block/transition/input/
mod.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16mod bytes;
17mod serialize;
18mod string;
19
20use console::{
21    network::prelude::*,
22    program::{Ciphertext, Plaintext, TransitionLeaf},
23    types::Field,
24};
25
26type Variant = u8;
27
28/// The transition input.
29#[derive(Clone, PartialEq, Eq)]
30pub enum Input<N: Network> {
31    /// The plaintext hash and (optional) plaintext.
32    Constant(Field<N>, Option<Plaintext<N>>),
33    /// The plaintext hash and (optional) plaintext.
34    Public(Field<N>, Option<Plaintext<N>>),
35    /// The ciphertext hash and (optional) ciphertext.
36    Private(Field<N>, Option<Ciphertext<N>>),
37    /// The serial number and tag of the record.
38    Record(Field<N>, Field<N>),
39    /// The hash of the external record's (function_id, record, tvk, input index).
40    ExternalRecord(Field<N>),
41}
42
43impl<N: Network> Input<N> {
44    /// Returns the variant of the input.
45    pub const fn variant(&self) -> Variant {
46        match self {
47            Input::Constant(..) => 0,
48            Input::Public(..) => 1,
49            Input::Private(..) => 2,
50            Input::Record(..) => 3, // <- Changing this will invalidate 'console::StatePath' and 'circuit::StatePath'.
51            Input::ExternalRecord(..) => 4,
52        }
53    }
54
55    /// Returns the ID of the input.
56    pub const fn id(&self) -> &Field<N> {
57        match self {
58            Input::Constant(id, ..) => id,
59            Input::Public(id, ..) => id,
60            Input::Private(id, ..) => id,
61            Input::Record(serial_number, ..) => serial_number,
62            Input::ExternalRecord(id) => id,
63        }
64    }
65
66    /// Returns the input as a transition leaf.
67    pub fn to_transition_leaf(&self, index: u8) -> TransitionLeaf<N> {
68        TransitionLeaf::new_with_version(index, self.variant(), *self.id())
69    }
70
71    /// Returns the tag, if the input is a record.
72    pub const fn tag(&self) -> Option<&Field<N>> {
73        match self {
74            Input::Record(_, tag) => Some(tag),
75            _ => None,
76        }
77    }
78
79    /// Returns the tag, if the input is a record, and consumes `self`.
80    pub fn into_tag(self) -> Option<Field<N>> {
81        match self {
82            Input::Record(_, tag) => Some(tag),
83            _ => None,
84        }
85    }
86
87    /// Returns the serial number, if the input is a record.
88    pub const fn serial_number(&self) -> Option<&Field<N>> {
89        match self {
90            Input::Record(serial_number, ..) => Some(serial_number),
91            _ => None,
92        }
93    }
94
95    /// Returns the serial number, if the input is a record, and consumes `self`.
96    pub fn into_serial_number(self) -> Option<Field<N>> {
97        match self {
98            Input::Record(serial_number, ..) => Some(serial_number),
99            _ => None,
100        }
101    }
102
103    /// Returns the public verifier inputs for the proof.
104    pub fn verifier_inputs(&self) -> impl '_ + Iterator<Item = N::Field> {
105        [Some(self.id()), self.tag()].into_iter().flatten().map(|id| **id)
106    }
107
108    /// Returns `true` if the input is well-formed.
109    /// If the optional value exists, this method checks that it hashes to the input ID.
110    pub fn verify(&self, function_id: Field<N>, tcm: &Field<N>, index: usize) -> bool {
111        // Ensure the hash of the value (if the value exists) is correct.
112        let result = || match self {
113            Input::Constant(hash, Some(input)) => {
114                match input.to_fields() {
115                    Ok(fields) => {
116                        // Construct the (console) input index as a field element.
117                        let index = Field::from_u16(index as u16);
118                        // Construct the preimage as `(function ID || input || tcm || index)`.
119                        let mut preimage = Vec::new();
120                        preimage.push(function_id);
121                        preimage.extend(fields);
122                        preimage.push(*tcm);
123                        preimage.push(index);
124                        // Ensure the hash matches.
125                        match N::hash_psd8(&preimage) {
126                            Ok(candidate_hash) => Ok(hash == &candidate_hash),
127                            Err(error) => Err(error),
128                        }
129                    }
130                    Err(error) => Err(error),
131                }
132            }
133            Input::Public(hash, Some(input)) => {
134                match input.to_fields() {
135                    Ok(fields) => {
136                        // Construct the (console) input index as a field element.
137                        let index = Field::from_u16(index as u16);
138                        // Construct the preimage as `(function ID || input || tcm || index)`.
139                        let mut preimage = Vec::new();
140                        preimage.push(function_id);
141                        preimage.extend(fields);
142                        preimage.push(*tcm);
143                        preimage.push(index);
144                        // Ensure the hash matches.
145                        match N::hash_psd8(&preimage) {
146                            Ok(candidate_hash) => Ok(hash == &candidate_hash),
147                            Err(error) => Err(error),
148                        }
149                    }
150                    Err(error) => Err(error),
151                }
152            }
153            Input::Private(hash, Some(value)) => {
154                match value.to_fields() {
155                    // Ensure the hash matches.
156                    Ok(fields) => match N::hash_psd8(&fields) {
157                        Ok(candidate_hash) => Ok(hash == &candidate_hash),
158                        Err(error) => Err(error),
159                    },
160                    Err(error) => Err(error),
161                }
162            }
163            Input::Constant(_, None) | Input::Public(_, None) | Input::Private(_, None) => {
164                // This enforces that the transition *must* contain the value for this transition input.
165                // A similar rule is enforced for the transition output.
166                bail!("A transition input value is missing")
167            }
168            Input::Record(_, _) | Input::ExternalRecord(_) => Ok(true),
169        };
170
171        match result() {
172            Ok(is_hash_valid) => is_hash_valid,
173            Err(error) => {
174                eprintln!("{error}");
175                false
176            }
177        }
178    }
179}
180
181#[cfg(test)]
182pub(crate) mod test_helpers {
183    use super::*;
184    use console::{network::MainnetV0, program::Literal};
185
186    type CurrentNetwork = MainnetV0;
187
188    /// Sample the transition inputs.
189    pub(crate) fn sample_inputs() -> Vec<(<CurrentNetwork as Network>::TransitionID, Input<CurrentNetwork>)> {
190        let rng = &mut TestRng::default();
191
192        // Sample a transition.
193        let transaction = crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0);
194        let transition = transaction.transitions().next().unwrap();
195
196        // Retrieve the transition ID and input.
197        let transition_id = *transition.id();
198        let input = transition.inputs().iter().next().unwrap().clone();
199
200        // Sample a random plaintext.
201        let plaintext = Plaintext::Literal(Literal::Field(Uniform::rand(rng)), Default::default());
202        let plaintext_hash = CurrentNetwork::hash_bhp1024(&plaintext.to_bits_le()).unwrap();
203        // Sample a random ciphertext.
204        let fields: Vec<_> = (0..10).map(|_| Uniform::rand(rng)).collect();
205        let ciphertext = Ciphertext::from_fields(&fields).unwrap();
206        let ciphertext_hash = CurrentNetwork::hash_bhp1024(&ciphertext.to_bits_le()).unwrap();
207
208        vec![
209            (transition_id, input),
210            (Uniform::rand(rng), Input::Constant(Uniform::rand(rng), None)),
211            (Uniform::rand(rng), Input::Constant(plaintext_hash, Some(plaintext.clone()))),
212            (Uniform::rand(rng), Input::Public(Uniform::rand(rng), None)),
213            (Uniform::rand(rng), Input::Public(plaintext_hash, Some(plaintext))),
214            (Uniform::rand(rng), Input::Private(Uniform::rand(rng), None)),
215            (Uniform::rand(rng), Input::Private(ciphertext_hash, Some(ciphertext))),
216            (Uniform::rand(rng), Input::Record(Uniform::rand(rng), Uniform::rand(rng))),
217            (Uniform::rand(rng), Input::ExternalRecord(Uniform::rand(rng))),
218        ]
219    }
220}