snarkvm_console_program/data/dynamic/future/
mod.rs1mod bytes;
17mod equal;
18mod parse;
19mod to_bits;
20mod to_fields;
21
22use crate::{Argument, Boolean, Field, Future, Identifier, Network, ProgramID, Result, ToField, ToFields};
23
24use snarkvm_console_algorithms::{Poseidon2, Poseidon8};
25use snarkvm_console_collections::merkle_tree::MerkleTree;
26use snarkvm_console_network::*;
27
28pub const FUTURE_ARGUMENT_TREE_DEPTH: u8 = 4;
30
31pub type FutureArgumentTree<E> = MerkleTree<E, Poseidon8<E>, Poseidon2<E>, FUTURE_ARGUMENT_TREE_DEPTH>;
33
34#[derive(Clone)]
50pub struct DynamicFuture<N: Network> {
51 program_name: Field<N>,
53 program_network: Field<N>,
55 function_name: Field<N>,
57 checksum: Field<N>,
59 arguments: Option<Vec<Argument<N>>>,
61}
62
63impl<N: Network> DynamicFuture<N> {
64 pub fn new_unchecked(
66 program_name: Field<N>,
67 program_network: Field<N>,
68 function_name: Field<N>,
69 checksum: Field<N>,
70 arguments: Option<Vec<Argument<N>>>,
71 ) -> Self {
72 Self { program_name, program_network, function_name, checksum, arguments }
73 }
74}
75
76impl<N: Network> DynamicFuture<N> {
77 pub const fn program_name(&self) -> &Field<N> {
79 &self.program_name
80 }
81
82 pub const fn program_network(&self) -> &Field<N> {
84 &self.program_network
85 }
86
87 pub const fn function_name(&self) -> &Field<N> {
89 &self.function_name
90 }
91
92 pub const fn checksum(&self) -> &Field<N> {
94 &self.checksum
95 }
96
97 pub const fn arguments(&self) -> &Option<Vec<Argument<N>>> {
99 &self.arguments
100 }
101}
102
103impl<N: Network> DynamicFuture<N> {
104 pub fn from_future(future: &Future<N>) -> Result<Self> {
106 let program_name = future.program_id().name().to_field()?;
108 let program_network = future.program_id().network().to_field()?;
110 let function_name = future.function_name().to_field()?;
112 let arguments = future.arguments().to_vec();
114
115 let mut bits = vec![];
117 u8::try_from(arguments.len())?.write_bits_le(&mut bits);
120 for argument in arguments.iter() {
123 argument.write_bits_le(&mut bits);
124 }
125 bits.resize(bits.len().div_ceil(8) * 8, false);
127
128 let hash_bits = N::hash_sha3_256(&bits)?;
130 let checksum = Field::<N>::from_bits_le(&hash_bits[..Field::<N>::size_in_data_bits()])?;
132
133 Ok(Self::new_unchecked(program_name, program_network, function_name, checksum, Some(arguments)))
134 }
135
136 pub fn to_future(&self) -> Result<Future<N>> {
138 let Some(arguments) = &self.arguments else {
140 bail!("Cannot convert dynamic future to a static future without the arguments being present");
141 };
142
143 Ok(Future::new(
144 ProgramID::try_from((
145 Identifier::from_field(&self.program_name)?,
146 Identifier::from_field(&self.program_network)?,
147 ))?,
148 Identifier::from_field(&self.function_name)?,
149 arguments.clone(),
150 ))
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use snarkvm_console_network::MainnetV0;
158
159 use crate::Plaintext;
160
161 use core::str::FromStr;
162
163 type CurrentNetwork = MainnetV0;
164
165 #[test]
166 fn test_data_depth() {
167 assert_eq!(CurrentNetwork::MAX_INPUTS.ilog2(), FUTURE_ARGUMENT_TREE_DEPTH as u32);
168 }
169
170 fn create_test_future(arguments: Vec<Argument<CurrentNetwork>>) -> Future<CurrentNetwork> {
171 Future::new(ProgramID::from_str("test.aleo").unwrap(), Identifier::from_str("foo").unwrap(), arguments)
172 }
173
174 fn assert_round_trip(arguments: Vec<Argument<CurrentNetwork>>) {
175 let future = create_test_future(arguments);
176 let dynamic = DynamicFuture::from_future(&future).unwrap();
177 let recovered = dynamic.to_future().unwrap();
178 assert_eq!(future.program_id(), recovered.program_id());
179 assert_eq!(future.function_name(), recovered.function_name());
180 assert_eq!(future.arguments().len(), recovered.arguments().len(), "Argument count must be preserved");
181 for (a, b) in future.arguments().iter().zip(recovered.arguments().iter()) {
182 assert!(*a.is_equal(b));
183 }
184 }
185
186 #[test]
187 fn test_round_trip_various_arguments() {
188 assert_round_trip(vec![]);
190
191 assert_round_trip(vec![
193 Argument::Plaintext(Plaintext::from_str("true").unwrap()),
194 Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
195 ]);
196
197 assert_round_trip(vec![Argument::Plaintext(Plaintext::from_str("{ x: 1field, y: 2field }").unwrap())]);
199
200 let inner =
202 Future::new(ProgramID::from_str("inner.aleo").unwrap(), Identifier::from_str("bar").unwrap(), vec![
203 Argument::Plaintext(Plaintext::from_str("42u64").unwrap()),
204 ]);
205 assert_round_trip(vec![Argument::Future(inner.clone())]);
206
207 assert_round_trip(vec![Argument::DynamicFuture(DynamicFuture::from_future(&inner).unwrap())]);
209
210 let max_args: Vec<_> =
212 (0..16).map(|i| Argument::Plaintext(Plaintext::from_str(&format!("{i}u64")).unwrap())).collect();
213 assert_round_trip(max_args);
214 }
215
216 #[test]
217 fn test_checksum_determinism() {
218 let args1 = vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())];
219 let args2 = vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())];
220 let args3 = vec![Argument::Plaintext(Plaintext::from_str("200u64").unwrap())];
221
222 let d1 = DynamicFuture::from_future(&create_test_future(args1)).unwrap();
223 let d2 = DynamicFuture::from_future(&create_test_future(args2)).unwrap();
224 let d3 = DynamicFuture::from_future(&create_test_future(args3)).unwrap();
225
226 assert_eq!(d1.checksum(), d2.checksum(), "Same arguments should produce same checksum");
227 assert_ne!(d1.checksum(), d3.checksum(), "Different arguments should produce different checksums");
228 }
229
230 #[test]
231 fn test_to_fields_is_deterministic() {
232 let args = vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())];
233 let dynamic = DynamicFuture::from_future(&create_test_future(args)).unwrap();
234
235 let fields1 = dynamic.to_fields().unwrap();
237 let fields2 = dynamic.to_fields().unwrap();
238 assert!(!fields1.is_empty(), "to_fields must return at least one field element");
239 assert_eq!(fields1, fields2, "to_fields must be deterministic");
240 }
241
242 #[test]
243 fn test_to_fields_differs_for_different_futures() {
244 let args_a = vec![Argument::Plaintext(Plaintext::from_str("1u64").unwrap())];
245 let args_b = vec![Argument::Plaintext(Plaintext::from_str("2u64").unwrap())];
246 let da = DynamicFuture::from_future(&create_test_future(args_a)).unwrap();
247 let db = DynamicFuture::from_future(&create_test_future(args_b)).unwrap();
248
249 assert_ne!(da.to_fields().unwrap(), db.to_fields().unwrap());
251 }
252}