Skip to main content

snarkvm_circuit_program/response/
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 from_outputs;
17mod process_outputs_from_callback;
18
19use crate::{Identifier, ProgramID, Value, compute_function_id};
20use snarkvm_circuit_network::Aleo;
21use snarkvm_circuit_types::{Address, Field, U16, environment::prelude::*};
22
23pub enum OutputID<A: Aleo> {
24    /// The hash of the constant output.
25    Constant(Field<A>),
26    /// The hash of the public output.
27    Public(Field<A>),
28    /// The ciphertext hash of the private output.
29    Private(Field<A>),
30    /// The `(commitment, checksum, sender_ciphertext)` tuple of the record output.
31    Record(Field<A>, Field<A>, Field<A>),
32    /// The hash of the external record's (function_id, record, tvk, output index).
33    ExternalRecord(Field<A>),
34    /// The hash of the future output.
35    Future(Field<A>),
36    /// The hash of the dynamic record's (function_id, dynamic record, tvk, output index).
37    DynamicRecord(Field<A>),
38    /// The hash of the dynamic future's (function_id, dynamic future, tvk, output index).
39    DynamicFuture(Field<A>),
40}
41
42impl<A: Aleo> Inject for OutputID<A> {
43    type Primitive = console::OutputID<A::Network>;
44
45    /// Initializes the output ID from the given mode and console output ID.
46    fn new(_: Mode, output: Self::Primitive) -> Self {
47        match output {
48            // Inject the expected hash as `Mode::Public`.
49            console::OutputID::Constant(field) => Self::Constant(Field::new(Mode::Public, field)),
50            // Inject the expected hash as `Mode::Public`.
51            console::OutputID::Public(field) => Self::Public(Field::new(Mode::Public, field)),
52            // Inject the ciphertext hash as `Mode::Public`.
53            console::OutputID::Private(field) => Self::Private(Field::new(Mode::Public, field)),
54            // Inject the expected commitment and checksum as `Mode::Public`.
55            console::OutputID::Record(commitment, checksum, sender_ciphertext) => Self::Record(
56                Field::new(Mode::Public, commitment),
57                Field::new(Mode::Public, checksum),
58                Field::new(Mode::Public, sender_ciphertext),
59            ),
60            // Inject the expected hash as `Mode::Public`.
61            console::OutputID::ExternalRecord(hash) => Self::ExternalRecord(Field::new(Mode::Public, hash)),
62            // Inject the expected hash as `Mode::Public`.
63            console::OutputID::Future(hash) => Self::Future(Field::new(Mode::Public, hash)),
64            // Inject the expected hash as `Mode::Public`.
65            console::OutputID::DynamicRecord(hash) => Self::DynamicRecord(Field::new(Mode::Public, hash)),
66            // Inject the expected hash as `Mode::Public`.
67            console::OutputID::DynamicFuture(hash) => Self::DynamicFuture(Field::new(Mode::Public, hash)),
68        }
69    }
70}
71
72impl<A: Aleo> OutputID<A> {
73    /// Initializes a constant output ID.
74    fn constant(expected_hash: Field<A>) -> Self {
75        // Inject the expected hash as `Mode::Public`.
76        let output_hash = Field::new(Mode::Public, expected_hash.eject_value());
77        // Ensure the injected hash matches the given hash.
78        A::assert_eq(&output_hash, expected_hash).expect("Constant output hash mismatch");
79        // Return the output ID.
80        Self::Constant(output_hash)
81    }
82
83    /// Initializes a public output ID.
84    fn public(expected_hash: Field<A>) -> Self {
85        // Inject the expected hash as `Mode::Public`.
86        let output_hash = Field::new(Mode::Public, expected_hash.eject_value());
87        // Ensure the injected hash matches the given hash.
88        A::assert_eq(&output_hash, expected_hash).expect("Public output hash mismatch");
89        // Return the output ID.
90        Self::Public(output_hash)
91    }
92
93    /// Initializes a private output ID.
94    fn private(expected_hash: Field<A>) -> Self {
95        // Inject the ciphertext hash as `Mode::Public`.
96        let output_hash = Field::new(Mode::Public, expected_hash.eject_value());
97        // Ensure the injected hash matches the given hash.
98        A::assert_eq(&output_hash, expected_hash).expect("Private output hash mismatch");
99        // Return the output ID.
100        Self::Private(output_hash)
101    }
102
103    /// Initializes a record output ID.
104    fn record(
105        expected_commitment: Field<A>,
106        expected_checksum: Field<A>,
107        expected_sender_ciphertext: Field<A>,
108    ) -> Self {
109        // Inject the expected commitment, checksum, and sender ciphertext as `Mode::Public`.
110        let output_commitment = Field::new(Mode::Public, expected_commitment.eject_value());
111        let output_checksum = Field::new(Mode::Public, expected_checksum.eject_value());
112        let output_sender_ciphertext = Field::new(Mode::Public, expected_sender_ciphertext.eject_value()); // Note: Set to `0field` here and in consensus to make optional or deactivated.
113        // Ensure the injected commitment and checksum matches the given commitment and checksum.
114        A::assert_eq(&output_commitment, expected_commitment).expect("Record output commitment mismatch");
115        A::assert_eq(&output_checksum, expected_checksum).expect("Record output checksum mismatch");
116        // Ensure the sender ciphertext matches, or the sender ciphertext is zero.
117        // Note: The option to allow a zero-value in the sender ciphertext allows
118        // this feature to become optional or deactivated in the future.
119        let is_sender_ciphertext_equal = output_sender_ciphertext.is_equal(&expected_sender_ciphertext);
120        let is_sender_ciphertext_zero = output_sender_ciphertext.is_zero();
121        A::assert(is_sender_ciphertext_equal | is_sender_ciphertext_zero).expect("Record sender ciphertext mismatch");
122        // Return the output ID.
123        Self::Record(output_commitment, output_checksum, output_sender_ciphertext)
124    }
125
126    /// Initializes an external record output ID.
127    fn external_record(expected_hash: Field<A>) -> Self {
128        // Inject the expected hash as `Mode::Public`.
129        let output_hash = Field::new(Mode::Public, expected_hash.eject_value());
130        // Ensure the injected hash matches the given commitment.
131        A::assert_eq(&output_hash, expected_hash).expect("ExternalRecord output hash mismatch");
132        // Return the output ID.
133        Self::ExternalRecord(output_hash)
134    }
135
136    /// Initializes a future output ID.
137    fn future(expected_hash: Field<A>) -> Self {
138        // Inject the expected hash as `Mode::Public`.
139        let output_hash = Field::new(Mode::Public, expected_hash.eject_value());
140        // Ensure the injected hash matches the given hash.
141        A::assert_eq(&output_hash, expected_hash).expect("Future output hash mismatch");
142        // Return the output ID.
143        Self::Future(output_hash)
144    }
145
146    /// Initializes a dynamic record output ID.
147    fn dynamic_record(expected_hash: Field<A>) -> Self {
148        // Inject the expected hash as `Mode::Public`.
149        let output_hash = Field::new(Mode::Public, expected_hash.eject_value());
150        // Ensure the injected hash matches the given hash.
151        A::assert_eq(&output_hash, expected_hash).expect("DynamicRecord output hash mismatch");
152        // Return the output ID.
153        Self::DynamicRecord(output_hash)
154    }
155
156    /// Initializes a dynamic future output ID.
157    fn dynamic_future(expected_hash: Field<A>) -> Self {
158        // Inject the expected hash as `Mode::Public`.
159        let output_hash = Field::new(Mode::Public, expected_hash.eject_value());
160        // Ensure the injected hash matches the given hash.
161        A::assert_eq(&output_hash, expected_hash).expect("DynamicFuture output hash mismatch");
162        // Return the output ID.
163        Self::DynamicFuture(output_hash)
164    }
165}
166
167impl<A: Aleo> Eject for OutputID<A> {
168    type Primitive = console::OutputID<A::Network>;
169
170    /// Ejects the mode of the output ID.
171    fn eject_mode(&self) -> Mode {
172        match self {
173            Self::Constant(field) => field.eject_mode(),
174            Self::Public(field) => field.eject_mode(),
175            Self::Private(field) => field.eject_mode(),
176            Self::Record(commitment, checksum, sender_ciphertext) => {
177                Mode::combine(commitment.eject_mode(), [checksum.eject_mode(), sender_ciphertext.eject_mode()])
178            }
179            Self::ExternalRecord(hash) => hash.eject_mode(),
180            Self::Future(hash) => hash.eject_mode(),
181            Self::DynamicRecord(hash) => hash.eject_mode(),
182            Self::DynamicFuture(hash) => hash.eject_mode(),
183        }
184    }
185
186    /// Ejects the output ID as a primitive.
187    fn eject_value(&self) -> Self::Primitive {
188        match self {
189            Self::Constant(field) => console::OutputID::Constant(field.eject_value()),
190            Self::Public(field) => console::OutputID::Public(field.eject_value()),
191            Self::Private(field) => console::OutputID::Private(field.eject_value()),
192            Self::Record(commitment, checksum, sender_ciphertext) => console::OutputID::Record(
193                commitment.eject_value(),
194                checksum.eject_value(),
195                sender_ciphertext.eject_value(),
196            ),
197            Self::ExternalRecord(hash) => console::OutputID::ExternalRecord(hash.eject_value()),
198            Self::Future(hash) => console::OutputID::Future(hash.eject_value()),
199            Self::DynamicRecord(hash) => console::OutputID::DynamicRecord(hash.eject_value()),
200            Self::DynamicFuture(hash) => console::OutputID::DynamicFuture(hash.eject_value()),
201        }
202    }
203}
204
205pub struct Response<A: Aleo> {
206    /// The function output IDs.
207    output_ids: Vec<OutputID<A>>,
208    /// The function outputs.
209    outputs: Vec<Value<A>>,
210}
211
212impl<A: Aleo> Response<A> {
213    /// Returns the output IDs for the transition.
214    pub fn output_ids(&self) -> &[OutputID<A>] {
215        &self.output_ids
216    }
217
218    /// Returns the function outputs.
219    pub fn outputs(&self) -> &[Value<A>] {
220        &self.outputs
221    }
222}
223
224impl<A: Aleo> Eject for Response<A> {
225    type Primitive = console::Response<A::Network>;
226
227    /// Ejects the mode of the response.
228    fn eject_mode(&self) -> Mode {
229        Mode::combine(self.output_ids.eject_mode(), [self.outputs.eject_mode()])
230    }
231
232    /// Ejects the response as a primitive.
233    fn eject_value(&self) -> Self::Primitive {
234        Self::Primitive::from((
235            self.output_ids.iter().map(|output_id| output_id.eject_value()).collect(),
236            self.outputs.eject_value(),
237        ))
238    }
239}