Skip to main content

snarkvm_synthesizer_process/stack/helpers/
sample.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> Stack<N> {
19    /// Samples a plaintext value according to the given plaintext type.
20    pub fn sample_plaintext<R: Rng + CryptoRng>(
21        &self,
22        plaintext_type: &PlaintextType<N>,
23        rng: &mut R,
24    ) -> Result<Plaintext<N>> {
25        // Sample a plaintext value.
26        let plaintext = self.sample_plaintext_internal(plaintext_type, 0, rng)?;
27        // Ensure the plaintext value matches the plaintext type.
28        self.matches_plaintext(&plaintext, plaintext_type)?;
29        // Return the plaintext value.
30        Ok(plaintext)
31    }
32
33    /// Samples a future value according to the given future type.
34    pub fn sample_future<R: Rng + CryptoRng>(&self, locator: &Locator<N>, rng: &mut R) -> Result<Future<N>> {
35        // Sample a future value.
36        let future = self.sample_future_internal(locator, 0, rng)?;
37        // Ensure the future value matches the future type.
38        self.matches_future(&future, locator)?;
39        // Return the future value.
40        Ok(future)
41    }
42
43    /// Samples a dynamic future value.
44    pub fn sample_dynamic_future<R: Rng + CryptoRng>(&self, rng: &mut R) -> Result<DynamicFuture<N>> {
45        // Sample a random program name.
46        let program_name = Field::rand(rng);
47        // Use the `.aleo` program network.
48        let program_network = Identifier::from_str("aleo").unwrap().to_field()?; // Safe: "aleo" is always a valid identifier.
49        // Sample a random function name.
50        let function_name = Field::rand(rng);
51        // Sample a random argument root.
52        let argument_root = Field::rand(rng);
53
54        Ok(DynamicFuture::new_unchecked(program_name, program_network, function_name, argument_root, None))
55    }
56
57    /// Samples a dynamic record value.
58    pub fn sample_dynamic_record<R: Rng + CryptoRng>(&self, rng: &mut R) -> Result<DynamicRecord<N>> {
59        // Sample a random address.
60        let owner = Address::rand(rng);
61        // Sample a random root.
62        let root = Field::rand(rng);
63        // Sample a random nonce.
64        let nonce = Group::<N>::rand(rng);
65        // Sample a random version.
66        let version = U8::<N>::rand(rng);
67
68        Ok(DynamicRecord::new_unchecked(owner, root, nonce, version, None))
69    }
70
71    /// Returns a record for the given record name.
72    pub(crate) fn sample_record_internal<R: Rng + CryptoRng>(
73        &self,
74        burner_address: &Address<N>,
75        record_name: &Identifier<N>,
76        nonce: Group<N>,
77        depth: usize,
78        rng: &mut R,
79    ) -> Result<Record<N, Plaintext<N>>> {
80        // If the depth exceeds the maximum depth, then the plaintext type is invalid.
81        ensure!(depth <= N::MAX_DATA_DEPTH, "Plaintext exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
82
83        // Retrieve the record type from the program.
84        let record_type = self.program.get_record(record_name)?;
85
86        // Initialize the owner based on the visibility.
87        let owner = match record_type.owner().is_public() {
88            true => RecordOwner::Public(*burner_address),
89            false => RecordOwner::Private(Plaintext::Literal(Literal::Address(*burner_address), Default::default())),
90        };
91
92        // Initialize the record data according to the defined type.
93        let data = record_type
94            .entries()
95            .iter()
96            .map(|(entry_name, entry_type)| {
97                // Sample the entry value.
98                let entry = self.sample_entry_internal(entry_type, depth + 1, rng)?;
99                // Return the entry.
100                Ok((*entry_name, entry))
101            })
102            .collect::<Result<IndexMap<_, _>>>()?;
103
104        // Set the record version.
105        let version = U8::<N>::rand(rng);
106
107        // Return the record.
108        Record::<N, Plaintext<N>>::from_plaintext(owner, data, nonce, version)
109    }
110
111    /// Samples an entry according to the given entry type.
112    fn sample_entry_internal<R: Rng + CryptoRng>(
113        &self,
114        entry_type: &EntryType<N>,
115        depth: usize,
116        rng: &mut R,
117    ) -> Result<Entry<N, Plaintext<N>>> {
118        // If the depth exceeds the maximum depth, then the entry type is invalid.
119        ensure!(depth <= N::MAX_DATA_DEPTH, "Entry exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
120
121        match entry_type {
122            EntryType::Constant(plaintext_type)
123            | EntryType::Public(plaintext_type)
124            | EntryType::Private(plaintext_type) => {
125                // Sample the plaintext value.
126                let plaintext = self.sample_plaintext_internal(plaintext_type, depth, rng)?;
127                // Return the entry.
128                match entry_type {
129                    EntryType::Constant(..) => Ok(Entry::Constant(plaintext)),
130                    EntryType::Public(..) => Ok(Entry::Public(plaintext)),
131                    EntryType::Private(..) => Ok(Entry::Private(plaintext)),
132                }
133            }
134        }
135    }
136
137    /// Samples a plaintext value according to the given plaintext type.
138    fn sample_plaintext_internal<R: Rng + CryptoRng>(
139        &self,
140        plaintext_type: &PlaintextType<N>,
141        depth: usize,
142        rng: &mut R,
143    ) -> Result<Plaintext<N>> {
144        // If the depth exceeds the maximum depth, then the plaintext type is invalid.
145        ensure!(depth <= N::MAX_DATA_DEPTH, "Plaintext exceeded maximum depth of {}", N::MAX_DATA_DEPTH);
146
147        // Sample the plaintext value.
148        let plaintext = match plaintext_type {
149            // Sample a literal.
150            PlaintextType::Literal(literal_type) => {
151                Plaintext::Literal(Literal::sample(*literal_type, rng), Default::default())
152            }
153            // Sample a struct.
154            PlaintextType::Struct(struct_name) => {
155                // Retrieve the struct.
156                let struct_ = self.program.get_struct(struct_name)?;
157                // Sample each member of the struct.
158                let members = struct_
159                    .members()
160                    .iter()
161                    .map(|(member_name, member_type)| {
162                        // Sample the member value.
163                        let member = self.sample_plaintext_internal(member_type, depth + 1, rng)?;
164                        // Return the member.
165                        Ok((*member_name, member))
166                    })
167                    .collect::<Result<IndexMap<_, _>>>()?;
168
169                Plaintext::Struct(members, Default::default())
170            }
171            PlaintextType::ExternalStruct(locator) => {
172                let external_stack = self.get_external_stack(locator.program_id())?;
173                let new_type = PlaintextType::Struct(*locator.resource());
174                return external_stack.sample_plaintext_internal(&new_type, depth, rng);
175            }
176            // Sample an array.
177            PlaintextType::Array(array_type) => {
178                // Sample each element of the array.
179                let elements = (0..**array_type.length())
180                    .map(|_| {
181                        // Sample the element value.
182                        self.sample_plaintext_internal(array_type.next_element_type(), depth + 1, rng)
183                    })
184                    .collect::<Result<Vec<_>>>()?;
185
186                Plaintext::Array(elements, Default::default())
187            }
188        };
189        // Return the plaintext.
190        Ok(plaintext)
191    }
192
193    /// Samples a future value according to the given locator.
194    fn sample_future_internal<R: Rng + CryptoRng>(
195        &self,
196        locator: &Locator<N>,
197        depth: usize,
198        rng: &mut R,
199    ) -> Result<Future<N>> {
200        // Retrieve the external stack, if needed.
201        let external_stack = match locator.program_id() == self.program_id() {
202            true => None,
203            // Attention - This method must fail here and early return if the external program is missing.
204            // Otherwise, this method will proceed to look for the requested function in its own program.
205            false => Some(self.get_external_stack(locator.program_id())?),
206        };
207        // Retrieve the associated function.
208        let function = match &external_stack {
209            Some(external_stack) => external_stack.get_function_ref(locator.resource())?,
210            None => self.get_function_ref(locator.resource())?,
211        };
212
213        // Retrieve the finalize inputs.
214        let inputs = match function.finalize_logic() {
215            Some(finalize_logic) => finalize_logic.inputs(),
216            None => bail!("Function '{locator}' does not have a finalize block"),
217        };
218
219        // Sample the arguments using the external stack if the future is from an external program.
220        let arguments = inputs
221            .into_iter()
222            .map(|input| {
223                match input.finalize_type() {
224                    FinalizeType::Plaintext(plaintext_type) => {
225                        // Sample the plaintext value using the appropriate stack.
226                        let plaintext = match &external_stack {
227                            Some(external_stack) => {
228                                external_stack.sample_plaintext_internal(plaintext_type, depth + 1, rng)?
229                            }
230                            None => self.sample_plaintext_internal(plaintext_type, depth + 1, rng)?,
231                        };
232                        // Return the argument.
233                        Ok(Argument::Plaintext(plaintext))
234                    }
235                    FinalizeType::Future(locator) => {
236                        // Sample the future value using the appropriate stack.
237                        let future = match &external_stack {
238                            Some(external_stack) => external_stack.sample_future_internal(locator, depth + 1, rng)?,
239                            None => self.sample_future_internal(locator, depth + 1, rng)?,
240                        };
241                        // Return the argument.
242                        Ok(Argument::Future(future))
243                    }
244                    FinalizeType::DynamicFuture => {
245                        // Sample the dynamic future value.
246                        let dynamic_future = self.sample_dynamic_future(rng)?;
247                        // Return the argument.
248                        Ok(Argument::DynamicFuture(dynamic_future))
249                    }
250                }
251            })
252            .collect::<Result<Vec<_>>>()?;
253
254        Ok(Future::new(*locator.program_id(), *locator.resource(), arguments))
255    }
256}