Skip to main content

snarkvm_console_program/data/future/
argument.rs

1// Copyright (c) 2019-2026 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
16use super::*;
17
18/// An argument passed into a future.
19#[derive(Clone, Debug)]
20pub enum Argument<N: Network> {
21    /// A plaintext value.
22    Plaintext(Plaintext<N>),
23    /// A future.
24    Future(Future<N>),
25    /// A dynamic future.
26    DynamicFuture(DynamicFuture<N>),
27}
28
29impl<N: Network> Equal<Self> for Argument<N> {
30    type Output = Boolean<N>;
31
32    /// Returns `true` if `self` and `other` are equal.
33    fn is_equal(&self, other: &Self) -> Self::Output {
34        match (self, other) {
35            (Self::Plaintext(a), Self::Plaintext(b)) => a.is_equal(b),
36            (Self::Future(a), Self::Future(b)) => a.is_equal(b),
37            (Self::DynamicFuture(a), Self::DynamicFuture(b)) => a.is_equal(b),
38            (Self::Plaintext(..), _) | (Self::Future(..), _) | (Self::DynamicFuture(..), _) => Boolean::new(false),
39        }
40    }
41
42    /// Returns `true` if `self` and `other` are *not* equal.
43    fn is_not_equal(&self, other: &Self) -> Self::Output {
44        match (self, other) {
45            (Self::Plaintext(a), Self::Plaintext(b)) => a.is_not_equal(b),
46            (Self::Future(a), Self::Future(b)) => a.is_not_equal(b),
47            (Self::DynamicFuture(a), Self::DynamicFuture(b)) => a.is_not_equal(b),
48            (Self::Plaintext(..), _) | (Self::Future(..), _) | (Self::DynamicFuture(..), _) => Boolean::new(true),
49        }
50    }
51}
52
53impl<N: Network> ToBits for Argument<N> {
54    /// Returns the argument as a list of **little-endian** bits.
55    #[inline]
56    fn write_bits_le(&self, vec: &mut Vec<bool>) {
57        match self {
58            Self::Plaintext(plaintext) => {
59                vec.push(false);
60                plaintext.write_bits_le(vec);
61            }
62            Self::Future(future) => {
63                vec.push(true);
64                future.write_bits_le(vec);
65            }
66            Self::DynamicFuture(dynamic_future) => {
67                vec.push(true);
68                // Note. This encoding is needed to uniquely disambiguate dynamic futures from static futures.
69                // This is sound because:
70                //  - a static future expects the program ID bits after the initial tag bit
71                //  - a program ID contains two `Identifier`s
72                //  - an `Identifier` cannot lead with a zero byte, since a leading zero byte implies an empty string.
73                // The 12 bits consist of: 8 bits for disambiguation (zero byte) + 4 bits reserved for future variants.
74                vec.extend(std::iter::repeat_n(false, 12));
75                dynamic_future.write_bits_le(vec);
76            }
77        }
78    }
79
80    /// Returns the argument as a list of **big-endian** bits.
81    #[inline]
82    fn write_bits_be(&self, vec: &mut Vec<bool>) {
83        match self {
84            Self::Plaintext(plaintext) => {
85                vec.push(false);
86                plaintext.write_bits_be(vec);
87            }
88            Self::Future(future) => {
89                vec.push(true);
90                future.write_bits_be(vec);
91            }
92            Self::DynamicFuture(dynamic_future) => {
93                vec.push(true);
94                // Note. This encoding is needed to uniquely disambiguate dynamic futures from static futures.
95                // This is sound because:
96                //  - a static future expects the program ID bits after the initial tag bit
97                //  - a program ID contains two `Identifier`s
98                //  - an `Identifier` cannot lead with a zero byte, since a leading zero byte implies an empty string.
99                // The 12 bits consist of: 8 bits for disambiguation (zero byte) + 4 bits reserved for future variants.
100                vec.extend(std::iter::repeat_n(false, 12));
101                dynamic_future.write_bits_be(vec);
102            }
103        }
104    }
105}
106
107impl<N: Network> ToFields for Argument<N> {
108    type Field = Field<N>;
109
110    /// Returns this plaintext as a list of field elements.
111    fn to_fields(&self) -> Result<Vec<Self::Field>> {
112        // Encode the data as little-endian bits.
113        let mut bits_le = self.to_bits_le();
114        // Adds one final bit to the data, to serve as a terminus indicator.
115        // During decryption, this final bit ensures we've reached the end.
116        bits_le.push(true);
117        // Pack the bits into field elements.
118        let fields = bits_le
119            .chunks(Field::<N>::size_in_data_bits())
120            .map(Field::<N>::from_bits_le)
121            .collect::<Result<Vec<_>>>()?;
122        // Ensure the number of field elements does not exceed the maximum allowed size.
123        match fields.len() <= N::MAX_DATA_SIZE_IN_FIELDS as usize {
124            true => Ok(fields),
125            false => bail!("Argument exceeds maximum allowed size"),
126        }
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use snarkvm_console_network::MainnetV0;
134
135    use core::str::FromStr;
136
137    type CurrentNetwork = MainnetV0;
138
139    #[test]
140    fn test_plaintext_argument_bit_encoding() {
141        // Create a plaintext argument.
142        let plaintext = Plaintext::<CurrentNetwork>::from_str("42u64").unwrap();
143        let argument = Argument::Plaintext(plaintext);
144
145        // Get the bits.
146        let bits = argument.to_bits_le();
147
148        // The first bit should be false (plaintext tag).
149        assert!(!bits[0], "Plaintext argument should start with false tag bit");
150    }
151
152    #[test]
153    fn test_future_argument_bit_encoding() {
154        // Create a future argument.
155        let future = Future::<CurrentNetwork>::new(
156            ProgramID::from_str("test.aleo").unwrap(),
157            Identifier::from_str("foo").unwrap(),
158            vec![],
159        );
160        let argument = Argument::Future(future);
161
162        // Get the bits.
163        let bits = argument.to_bits_le();
164
165        // The first bit should be true (future tag).
166        assert!(bits[0], "Future argument should start with true tag bit");
167
168        // The next bits should be the program ID, which cannot start with a zero byte.
169        // Check that at least one of the first 8 bits after the tag is true.
170        let first_byte_bits = &bits[1..9];
171        assert!(first_byte_bits.iter().any(|&b| b), "Static future's program ID should not start with a zero byte");
172    }
173
174    #[test]
175    fn test_dynamic_future_argument_bit_encoding() {
176        // Create a future and convert to dynamic.
177        let future = Future::<CurrentNetwork>::new(
178            ProgramID::from_str("test.aleo").unwrap(),
179            Identifier::from_str("foo").unwrap(),
180            vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
181        );
182        let dynamic_future = DynamicFuture::from_future(&future).unwrap();
183        let argument = Argument::DynamicFuture(dynamic_future);
184
185        // Get the bits.
186        let bits = argument.to_bits_le();
187
188        // The first bit should be true (future tag).
189        assert!(bits[0], "DynamicFuture argument should start with true tag bit");
190
191        // The next 12 bits should all be false (8 for disambiguation + 4 reserved for future variants).
192        for (i, &bit) in bits[1..13].iter().enumerate() {
193            assert!(!bit, "DynamicFuture should have false bit at position {} (1-indexed: {})", i, i + 1);
194        }
195    }
196
197    #[test]
198    fn test_dynamic_future_distinguishable_from_static_future() {
199        // Create a static future.
200        let static_future = Future::<CurrentNetwork>::new(
201            ProgramID::from_str("test.aleo").unwrap(),
202            Identifier::from_str("foo").unwrap(),
203            vec![],
204        );
205        let static_argument = Argument::Future(static_future.clone());
206
207        // Create a dynamic future from the same future.
208        let dynamic_future = DynamicFuture::from_future(&static_future).unwrap();
209        let dynamic_argument = Argument::DynamicFuture(dynamic_future);
210
211        // Get the bits for both.
212        let static_bits = static_argument.to_bits_le();
213        let dynamic_bits = dynamic_argument.to_bits_le();
214
215        // Both should start with true (future tag).
216        assert!(static_bits[0], "Static future should start with true tag bit");
217        assert!(dynamic_bits[0], "Dynamic future should start with true tag bit");
218
219        // The disambiguation should occur in the next 12 bits.
220        // Static future: program ID bits (cannot be all zeros in first byte).
221        // Dynamic future: 12 zero bits.
222        let static_first_12 = &static_bits[1..13];
223        let dynamic_first_12 = &dynamic_bits[1..13];
224
225        // Static future should have at least one true bit in first 8 bits (first byte of program ID).
226        assert!(
227            static_first_12[..8].iter().any(|&b| b),
228            "Static future should have non-zero first byte (program ID identifier)"
229        );
230
231        // Dynamic future should have all false bits in first 12 bits.
232        assert!(dynamic_first_12.iter().all(|&b| !b), "Dynamic future should have all-zero disambiguation prefix");
233    }
234
235    /// Verifies equality semantics: same values are equal, different values are not.
236    fn check_equality(
237        same1: &Argument<CurrentNetwork>,
238        same2: &Argument<CurrentNetwork>,
239        different: &Argument<CurrentNetwork>,
240    ) {
241        // Same values should be equal.
242        assert!(*same1.is_equal(same2));
243        assert!(!*same1.is_not_equal(same2));
244        // Different values should not be equal.
245        assert!(!*same1.is_equal(different));
246        assert!(*same1.is_not_equal(different));
247    }
248
249    #[test]
250    fn test_argument_equality() {
251        // Test plaintext equality.
252        let p1 = Argument::Plaintext(Plaintext::from_str("42u64").unwrap());
253        let p2 = Argument::Plaintext(Plaintext::from_str("42u64").unwrap());
254        let p3 = Argument::Plaintext(Plaintext::from_str("100u64").unwrap());
255        check_equality(&p1, &p2, &p3);
256
257        // Test future equality.
258        let make_future = |name: &str| {
259            Future::<CurrentNetwork>::new(
260                ProgramID::from_str("test.aleo").unwrap(),
261                Identifier::from_str(name).unwrap(),
262                vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
263            )
264        };
265        let f1 = Argument::Future(make_future("foo"));
266        let f2 = Argument::Future(make_future("foo"));
267        let f3 = Argument::Future(make_future("bar"));
268        check_equality(&f1, &f2, &f3);
269
270        // Test dynamic future equality.
271        let make_dynamic = |program: &str| {
272            let future = Future::<CurrentNetwork>::new(
273                ProgramID::from_str(program).unwrap(),
274                Identifier::from_str("foo").unwrap(),
275                vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
276            );
277            DynamicFuture::from_future(&future).unwrap()
278        };
279        let d1 = Argument::DynamicFuture(make_dynamic("test.aleo"));
280        let d2 = Argument::DynamicFuture(make_dynamic("test.aleo"));
281        let d3 = Argument::DynamicFuture(make_dynamic("other.aleo"));
282        check_equality(&d1, &d2, &d3);
283    }
284
285    #[test]
286    fn test_argument_equality_cross_variant() {
287        // Create arguments of different variants with comparable content.
288        let plaintext = Plaintext::<CurrentNetwork>::from_str("42u64").unwrap();
289        let future = Future::<CurrentNetwork>::new(
290            ProgramID::from_str("test.aleo").unwrap(),
291            Identifier::from_str("foo").unwrap(),
292            vec![],
293        );
294        let dynamic_future = DynamicFuture::from_future(&future).unwrap();
295
296        let arg_plaintext = Argument::Plaintext(plaintext);
297        let arg_future = Argument::Future(future);
298        let arg_dynamic = Argument::DynamicFuture(dynamic_future);
299
300        // Cross-variant comparisons should always return false for is_equal.
301        assert!(!*arg_plaintext.is_equal(&arg_future));
302        assert!(!*arg_plaintext.is_equal(&arg_dynamic));
303        assert!(!*arg_future.is_equal(&arg_plaintext));
304        assert!(!*arg_future.is_equal(&arg_dynamic));
305        assert!(!*arg_dynamic.is_equal(&arg_plaintext));
306        assert!(!*arg_dynamic.is_equal(&arg_future));
307
308        // Cross-variant comparisons should always return true for is_not_equal.
309        assert!(*arg_plaintext.is_not_equal(&arg_future));
310        assert!(*arg_plaintext.is_not_equal(&arg_dynamic));
311        assert!(*arg_future.is_not_equal(&arg_plaintext));
312        assert!(*arg_future.is_not_equal(&arg_dynamic));
313        assert!(*arg_dynamic.is_not_equal(&arg_plaintext));
314        assert!(*arg_dynamic.is_not_equal(&arg_future));
315    }
316}