Skip to main content

snarkvm_circuit_program/data/dynamic/future/
mod.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
16mod equal;
17mod to_bits;
18mod to_fields;
19
20use snarkvm_circuit_network::Aleo;
21use snarkvm_circuit_types::{Boolean, Field, environment::prelude::*};
22
23/// A dynamic future is a fixed-size representation of a future. Like static
24/// `Future`s, a dynamic future contains a program name, program network, and function name. These
25/// are however represented as `Field` elements as opposed to `Identifier`s to
26/// ensure a fixed size. Dynamic futures also store a checksum of the
27/// arguments to the future instead of the arguments themselves. This ensures
28/// that all dynamic futures have a constant size, regardless of the amount of
29/// data they contain.
30///
31/// Note: The checksum is never computed in circuit. It is computed in console
32/// as `truncate_252(Sha3_256(length_prefix || type_prefixed_argument_bits))` and
33/// injected as a witness field element.
34#[derive(Clone)]
35pub struct DynamicFuture<A: Aleo> {
36    /// The program name.
37    program_name: Field<A>,
38    /// The program network.
39    program_network: Field<A>,
40    /// The function name.
41    function_name: Field<A>,
42    /// The checksum of the arguments.
43    checksum: Field<A>,
44    /// The optional console arguments.
45    /// Note: This is NOT part of the circuit representation.
46    arguments: Option<Vec<console::Argument<A::Network>>>,
47}
48
49impl<A: Aleo> Inject for DynamicFuture<A> {
50    type Primitive = console::DynamicFuture<A::Network>;
51
52    /// Initializes a circuit of the given mode and future.
53    fn new(mode: Mode, value: Self::Primitive) -> Self {
54        DynamicFuture {
55            program_name: Inject::new(mode, *value.program_name()),
56            program_network: Inject::new(mode, *value.program_network()),
57            function_name: Inject::new(mode, *value.function_name()),
58            checksum: Inject::new(mode, *value.checksum()),
59            arguments: value.arguments().clone(),
60        }
61    }
62}
63
64impl<A: Aleo> DynamicFuture<A> {
65    /// Returns the program name.
66    pub const fn program_name(&self) -> &Field<A> {
67        &self.program_name
68    }
69
70    /// Returns the program network.
71    pub const fn program_network(&self) -> &Field<A> {
72        &self.program_network
73    }
74
75    /// Returns the function name.
76    pub const fn function_name(&self) -> &Field<A> {
77        &self.function_name
78    }
79
80    /// Returns the checksum of the arguments.
81    pub const fn checksum(&self) -> &Field<A> {
82        &self.checksum
83    }
84
85    /// Returns the console arguments.
86    pub const fn arguments(&self) -> &Option<Vec<console::Argument<A::Network>>> {
87        &self.arguments
88    }
89}
90
91impl<A: Aleo> Eject for DynamicFuture<A> {
92    type Primitive = console::DynamicFuture<A::Network>;
93
94    /// Ejects the mode of the dynamic future.
95    fn eject_mode(&self) -> Mode {
96        let program_name_mode = Eject::eject_mode(self.program_name());
97        let program_network_mode = Eject::eject_mode(self.program_network());
98        let function_name_mode = Eject::eject_mode(self.function_name());
99        let checksum_mode = Eject::eject_mode(self.checksum());
100        Mode::combine(program_name_mode, [program_network_mode, function_name_mode, checksum_mode])
101    }
102
103    /// Ejects the dynamic future.
104    fn eject_value(&self) -> Self::Primitive {
105        Self::Primitive::new_unchecked(
106            Eject::eject_value(self.program_name()),
107            Eject::eject_value(self.program_network()),
108            Eject::eject_value(self.function_name()),
109            Eject::eject_value(self.checksum()),
110            self.arguments.clone(),
111        )
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use crate::{Circuit, console::ToFields as ConsoleToFields};
119    use snarkvm_circuit_types::environment::Inject;
120
121    use core::str::FromStr;
122
123    type CurrentNetwork = <Circuit as Environment>::Network;
124    type ConsoleFuture = console::Future<CurrentNetwork>;
125    type ConsoleDynamicFuture = console::DynamicFuture<CurrentNetwork>;
126
127    fn create_test_future(args: Vec<console::Argument<CurrentNetwork>>) -> ConsoleFuture {
128        ConsoleFuture::new(
129            console::ProgramID::from_str("test.aleo").unwrap(),
130            console::Identifier::from_str("foo").unwrap(),
131            args,
132        )
133    }
134
135    /// Verifies that injecting a console DynamicFuture into a circuit and ejecting it
136    /// reproduces the original value.
137    fn check_inject_eject(console_dynamic: &ConsoleDynamicFuture) {
138        let circuit_dynamic = DynamicFuture::<Circuit>::new(Mode::Private, console_dynamic.clone());
139
140        assert_eq!(circuit_dynamic.program_name().eject_value(), *console_dynamic.program_name());
141        assert_eq!(circuit_dynamic.program_network().eject_value(), *console_dynamic.program_network());
142        assert_eq!(circuit_dynamic.function_name().eject_value(), *console_dynamic.function_name());
143        assert_eq!(circuit_dynamic.checksum().eject_value(), *console_dynamic.checksum());
144
145        let ejected = circuit_dynamic.eject_value();
146        assert_eq!(ejected.program_name(), console_dynamic.program_name());
147        assert_eq!(ejected.program_network(), console_dynamic.program_network());
148        assert_eq!(ejected.function_name(), console_dynamic.function_name());
149        assert_eq!(ejected.checksum(), console_dynamic.checksum());
150
151        Circuit::reset();
152    }
153
154    /// Verifies that circuit to_bits_le produces the same bits as the console equivalent.
155    fn check_to_bits_le(console_dynamic: &ConsoleDynamicFuture) {
156        let circuit_dynamic = DynamicFuture::<Circuit>::new(Mode::Private, console_dynamic.clone());
157
158        Circuit::scope("to_bits_le", || {
159            let circuit_bits = circuit_dynamic.to_bits_le();
160            let console_bits = console_dynamic.to_bits_le();
161            for (circuit_bit, console_bit) in circuit_bits.iter().zip_eq(console_bits.iter()) {
162                assert_eq!(circuit_bit.eject_value(), *console_bit, "Circuit and console bits must match");
163            }
164        });
165
166        Circuit::reset();
167    }
168
169    /// Verifies that circuit to_fields produces the same field elements as the console equivalent.
170    fn check_to_fields(console_dynamic: &ConsoleDynamicFuture) {
171        let circuit_dynamic = DynamicFuture::<Circuit>::new(Mode::Private, console_dynamic.clone());
172
173        Circuit::scope("to_fields", || {
174            let circuit_fields = circuit_dynamic.to_fields();
175            let console_fields = console_dynamic.to_fields().unwrap();
176            for (circuit_field, console_field) in circuit_fields.iter().zip_eq(console_fields.iter()) {
177                assert_eq!(circuit_field.eject_value(), *console_field, "Circuit and console fields must match");
178            }
179        });
180
181        Circuit::reset();
182    }
183
184    #[test]
185    fn test_inject_eject_no_arguments() {
186        let future = create_test_future(vec![]);
187        let dynamic = ConsoleDynamicFuture::from_future(&future).unwrap();
188        check_inject_eject(&dynamic);
189    }
190
191    #[test]
192    fn test_inject_eject_with_arguments() {
193        let args = vec![
194            console::Argument::Plaintext(console::Plaintext::from_str("100u64").unwrap()),
195            console::Argument::Plaintext(console::Plaintext::from_str("true").unwrap()),
196        ];
197        let future = create_test_future(args);
198        let dynamic = ConsoleDynamicFuture::from_future(&future).unwrap();
199        check_inject_eject(&dynamic);
200    }
201
202    #[test]
203    fn test_to_bits_le_console_circuit_equivalence() {
204        let args = vec![console::Argument::Plaintext(console::Plaintext::from_str("42u64").unwrap())];
205        let future = create_test_future(args);
206        let dynamic = ConsoleDynamicFuture::from_future(&future).unwrap();
207        check_to_bits_le(&dynamic);
208    }
209
210    #[test]
211    fn test_to_fields_console_circuit_equivalence() {
212        let args = vec![console::Argument::Plaintext(console::Plaintext::from_str("42u64").unwrap())];
213        let future = create_test_future(args);
214        let dynamic = ConsoleDynamicFuture::from_future(&future).unwrap();
215        check_to_fields(&dynamic);
216    }
217
218    #[test]
219    fn test_to_fields_no_arguments() {
220        let future = create_test_future(vec![]);
221        let dynamic = ConsoleDynamicFuture::from_future(&future).unwrap();
222        check_to_fields(&dynamic);
223    }
224}