Skip to main content

snarkvm_console_program/data/future/
find.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
18impl<N: Network> Future<N> {
19    /// Returns a value from the given path.
20    pub fn find<A: Into<Access<N>> + Copy + Debug>(&self, path: &[A]) -> Result<Value<N>> {
21        // Ensure the path is not empty.
22        ensure!(!path.is_empty(), "Attempted to find an argument with an empty path.");
23
24        // A helper enum to track the argument.
25        enum ArgumentRefType<'a, N: Network> {
26            /// A plaintext type.
27            Plaintext(&'a Plaintext<N>),
28            /// A future.
29            Future(&'a Future<N>),
30            /// A dynamic future.
31            DynamicFuture(&'a DynamicFuture<N>),
32        }
33
34        // Initialize a value starting from the top-level.
35        let mut value = ArgumentRefType::Future(self);
36
37        // Iterate through the path to retrieve the value.
38        for access in path.iter() {
39            let access = (*access).into();
40            match (value, access) {
41                (ArgumentRefType::Plaintext(Plaintext::Struct(members, ..)), Access::Member(identifier)) => {
42                    match members.get(&identifier) {
43                        // Retrieve the member and update `value` for the next iteration.
44                        Some(member) => value = ArgumentRefType::Plaintext(member),
45                        // Halts if the member does not exist.
46                        None => bail!("Failed to locate member '{identifier}'"),
47                    }
48                }
49                (ArgumentRefType::Plaintext(Plaintext::Array(array, ..)), Access::Index(index)) => {
50                    match array.get(*index as usize) {
51                        // Retrieve the element and update `value` for the next iteration.
52                        Some(element) => value = ArgumentRefType::Plaintext(element),
53                        // Halts if the index is out of bounds.
54                        None => bail!("Index '{index}' is out of bounds"),
55                    }
56                }
57                (ArgumentRefType::Future(future), Access::Index(index)) => {
58                    match future.arguments.get(*index as usize) {
59                        // If the argument is a future, update `value` for the next iteration.
60                        Some(Argument::Future(future)) => value = ArgumentRefType::Future(future),
61                        // If the argument is a plaintext, update `value` for the next iteration.
62                        Some(Argument::Plaintext(plaintext)) => value = ArgumentRefType::Plaintext(plaintext),
63                        // If the argument is a dynamic future, update `value` for the next iteration.
64                        Some(Argument::DynamicFuture(dynamic_future)) => {
65                            value = ArgumentRefType::DynamicFuture(dynamic_future)
66                        }
67                        // Halts if the index is out of bounds.
68                        None => bail!("Index '{index}' is out of bounds"),
69                    }
70                }
71                _ => bail!("Invalid access `{access}`"),
72            }
73        }
74
75        match value {
76            ArgumentRefType::Plaintext(plaintext) => Ok(Value::Plaintext(plaintext.clone())),
77            ArgumentRefType::Future(future) => Ok(Value::Future(future.clone())),
78            ArgumentRefType::DynamicFuture(dynamic_future) => Ok(Value::DynamicFuture(dynamic_future.clone())),
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use snarkvm_console_network::MainnetV0;
87
88    use core::str::FromStr;
89
90    type CurrentNetwork = MainnetV0;
91
92    #[test]
93    fn test_find_plaintext_argument() {
94        // Create a future with plaintext arguments.
95        let future = Future::<CurrentNetwork>::new(
96            ProgramID::from_str("test.aleo").unwrap(),
97            Identifier::from_str("foo").unwrap(),
98            vec![
99                Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
100                Argument::Plaintext(Plaintext::from_str("200u64").unwrap()),
101            ],
102        );
103
104        // Access the first argument.
105        let value = future.find(&[Access::Index(U32::new(0))]).unwrap();
106        assert_eq!(value, Value::Plaintext(Plaintext::from_str("100u64").unwrap()));
107
108        // Access the second argument.
109        let value = future.find(&[Access::Index(U32::new(1))]).unwrap();
110        assert_eq!(value, Value::Plaintext(Plaintext::from_str("200u64").unwrap()));
111    }
112
113    #[test]
114    fn test_find_nested_future_argument() {
115        // Create an inner future.
116        let inner = Future::<CurrentNetwork>::new(
117            ProgramID::from_str("inner.aleo").unwrap(),
118            Identifier::from_str("bar").unwrap(),
119            vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
120        );
121
122        // Create an outer future with the inner future as an argument.
123        let outer = Future::<CurrentNetwork>::new(
124            ProgramID::from_str("outer.aleo").unwrap(),
125            Identifier::from_str("baz").unwrap(),
126            vec![Argument::Future(inner.clone())],
127        );
128
129        // Access the nested future.
130        let value = outer.find(&[Access::Index(U32::new(0))]).unwrap();
131        assert_eq!(value, Value::Future(inner));
132    }
133
134    #[test]
135    fn test_find_dynamic_future_argument() {
136        // Create an inner future and convert to dynamic.
137        let inner = Future::<CurrentNetwork>::new(
138            ProgramID::from_str("inner.aleo").unwrap(),
139            Identifier::from_str("bar").unwrap(),
140            vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
141        );
142        let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
143
144        // Create an outer future with the dynamic future as an argument.
145        let outer = Future::<CurrentNetwork>::new(
146            ProgramID::from_str("outer.aleo").unwrap(),
147            Identifier::from_str("baz").unwrap(),
148            vec![Argument::DynamicFuture(dynamic_inner.clone())],
149        );
150
151        // Access the dynamic future argument.
152        let value = outer.find(&[Access::Index(U32::new(0))]).unwrap();
153
154        // Verify the result is a dynamic future with matching fields.
155        match value {
156            Value::DynamicFuture(result) => {
157                assert_eq!(result.program_name(), dynamic_inner.program_name());
158                assert_eq!(result.program_network(), dynamic_inner.program_network());
159                assert_eq!(result.function_name(), dynamic_inner.function_name());
160                assert_eq!(result.checksum(), dynamic_inner.checksum());
161            }
162            _ => panic!("Expected DynamicFuture value"),
163        }
164    }
165
166    #[test]
167    fn test_find_mixed_arguments() {
168        // Create an inner future and dynamic future.
169        let inner = Future::<CurrentNetwork>::new(
170            ProgramID::from_str("inner.aleo").unwrap(),
171            Identifier::from_str("bar").unwrap(),
172            vec![],
173        );
174        let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
175
176        // Create a future with mixed argument types.
177        let future = Future::<CurrentNetwork>::new(
178            ProgramID::from_str("test.aleo").unwrap(),
179            Identifier::from_str("mixed").unwrap(),
180            vec![
181                Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
182                Argument::Future(inner.clone()),
183                Argument::DynamicFuture(dynamic_inner.clone()),
184            ],
185        );
186
187        // Access plaintext argument.
188        let value = future.find(&[Access::Index(U32::new(0))]).unwrap();
189        assert!(matches!(value, Value::Plaintext(_)));
190
191        // Access future argument.
192        let value = future.find(&[Access::Index(U32::new(1))]).unwrap();
193        assert!(matches!(value, Value::Future(_)));
194
195        // Access dynamic future argument.
196        let value = future.find(&[Access::Index(U32::new(2))]).unwrap();
197        assert!(matches!(value, Value::DynamicFuture(_)));
198    }
199
200    #[test]
201    fn test_find_out_of_bounds() {
202        // Create a future with one argument.
203        let future = Future::<CurrentNetwork>::new(
204            ProgramID::from_str("test.aleo").unwrap(),
205            Identifier::from_str("foo").unwrap(),
206            vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
207        );
208
209        // Try to access an out-of-bounds index.
210        let result = future.find(&[Access::Index(U32::new(5))]);
211        assert!(result.is_err());
212    }
213
214    #[test]
215    fn test_dynamic_future_fields_cannot_be_accessed() {
216        // Create a future with a plaintext argument and convert to dynamic.
217        let inner = Future::<CurrentNetwork>::new(
218            ProgramID::from_str("inner.aleo").unwrap(),
219            Identifier::from_str("bar").unwrap(),
220            vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
221        );
222        let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
223
224        // Create an outer future with the dynamic future as an argument.
225        let outer = Future::<CurrentNetwork>::new(
226            ProgramID::from_str("outer.aleo").unwrap(),
227            Identifier::from_str("baz").unwrap(),
228            vec![Argument::DynamicFuture(dynamic_inner)],
229        );
230
231        // Accessing the dynamic future itself should succeed.
232        let value = outer.find(&[Access::Index(U32::new(0))]).unwrap();
233        assert!(matches!(value, Value::DynamicFuture(_)));
234
235        // Attempting to access fields within the dynamic future should fail.
236        // Dynamic futures are opaque and do not expose their internal structure.
237        let result = outer.find(&[Access::Index(U32::new(0)), Access::Index(U32::new(0))]);
238        assert!(result.is_err());
239    }
240}