1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (C) 2019-2022 Aleo Systems Inc.
// This file is part of the snarkVM library.

// The snarkVM library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The snarkVM library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with the snarkVM library. If not, see <https://www.gnu.org/licenses/>.

use super::*;

impl<N: Network> Process<N> {
    /// Returns an additional fee given the credits record and the additional fee amount (in gates).
    #[inline]
    pub fn execute_additional_fee<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
        &self,
        private_key: &PrivateKey<N>,
        credits: Record<N, Plaintext<N>>,
        additional_fee_in_gates: u64,
        rng: &mut R,
    ) -> Result<(Response<N>, AdditionalFee<N>)> {
        // Ensure the additional fee has the correct program ID.
        let program_id = ProgramID::from_str("credits.aleo")?;
        // Ensure the additional fee has the correct function.
        let function_name = Identifier::from_str("fee")?;

        // Retrieve the input types.
        let input_types = self.get_program(&program_id)?.get_function(&function_name)?.input_types();
        // Construct the inputs.
        let inputs =
            vec![Value::Record(credits), Value::from_str(&format!("{}", U64::<N>::new(additional_fee_in_gates)))?];
        // Compute the request.
        let request = Request::sign(private_key, program_id, function_name, &inputs, &input_types, rng)?;
        // Initialize the authorization.
        let authorization = Authorization::new(&[request.clone()]);
        // Construct the call stack.
        let call_stack = CallStack::Authorize(vec![request], *private_key, authorization.clone());
        // Construct the authorization from the function.
        let _response = self.get_stack(&program_id)?.execute_function::<A, R>(call_stack, rng)?;

        // Retrieve the main request (without popping it).
        let request = authorization.peek_next()?;
        // Prepare the stack.
        let stack = self.get_stack(request.program_id())?;

        #[cfg(feature = "aleo-cli")]
        println!("{}", format!(" • Calling '{}/{}'...", request.program_id(), request.function_name()).dimmed());

        // Initialize the execution.
        let execution = Arc::new(RwLock::new(Execution::new()));
        // Execute the circuit.
        let response = stack.execute_function::<A, R>(CallStack::execute(authorization, execution.clone())?, rng)?;
        // Extract the execution.
        let execution = execution.read().clone();
        // Ensure the execution contains 1 transition.
        ensure!(execution.len() == 1, "Execution of '{}/{}' does not contain 1 transition", program_id, function_name);

        Ok((response, execution.peek()?))
    }

    /// Verifies the given additional fee is valid.
    #[inline]
    pub fn verify_additional_fee(&self, additional_fee: &AdditionalFee<N>) -> Result<()> {
        #[cfg(debug_assertions)]
        println!("Verifying additional fee for {}/{}...", additional_fee.program_id(), additional_fee.function_name());

        // Ensure the additional fee has the correct program ID.
        let fee_program_id = ProgramID::from_str("credits.aleo")?;
        ensure!(*additional_fee.program_id() == fee_program_id, "Incorrect program ID for additional fee");

        // Ensure the additional fee has the correct function.
        let fee_function = Identifier::from_str("fee")?;
        ensure!(*additional_fee.function_name() == fee_function, "Incorrect function name for additional fee");

        // Ensure the transition ID of the additional fee is correct.
        ensure!(**additional_fee.id() == additional_fee.to_root()?, "Transition ID of the additional fee is incorrect");

        // Ensure the number of inputs is within the allowed range.
        ensure!(additional_fee.inputs().len() <= N::MAX_INPUTS, "Additional fee exceeded maximum number of inputs");
        // Ensure the number of outputs is within the allowed range.
        ensure!(additional_fee.outputs().len() <= N::MAX_INPUTS, "Additional fee exceeded maximum number of outputs");

        // Ensure each input is valid.
        if additional_fee.inputs().iter().enumerate().any(|(index, input)| !input.verify(additional_fee.tcm(), index)) {
            bail!("Failed to verify an additional fee input")
        }
        // Ensure each output is valid.
        let num_inputs = additional_fee.inputs().len();
        if additional_fee
            .outputs()
            .iter()
            .enumerate()
            .any(|(index, output)| !output.verify(additional_fee.tcm(), num_inputs + index))
        {
            bail!("Failed to verify an additional fee output")
        }

        // Ensure the fee is not negative.
        ensure!(additional_fee.fee() >= &0, "The fee must be zero or positive");

        // Compute the x- and y-coordinate of `tpk`.
        let (tpk_x, tpk_y) = additional_fee.tpk().to_xy_coordinate();

        // Construct the public inputs to verify the proof.
        let mut inputs = vec![N::Field::one(), *tpk_x, *tpk_y, **additional_fee.tcm()];
        // Extend the inputs with the input IDs.
        inputs.extend(additional_fee.inputs().iter().flat_map(|input| input.verifier_inputs()));
        // Extend the inputs with the output IDs.
        inputs.extend(additional_fee.outputs().iter().flat_map(|output| output.verifier_inputs()));
        // Extend the inputs with the fee.
        inputs.push(*I64::<N>::new(*additional_fee.fee()).to_field()?);

        // Retrieve the stack.
        let stack = self.get_stack(additional_fee.program_id())?;
        // Retrieve the function from the stack.
        let function = stack.get_function(additional_fee.function_name())?;
        // Ensure the number of function calls in this function is 1.
        if stack.get_number_of_calls(function.name())? != 1 {
            bail!("The number of function calls in '{}/{}' should be 1", stack.program_id(), function.name())
        }

        #[cfg(debug_assertions)]
        println!("Additional fee public inputs ({} elements): {:#?}", inputs.len(), inputs);

        // Retrieve the verifying key.
        let verifying_key = self.get_verifying_key(stack.program_id(), function.name())?;
        // Ensure the proof is valid.
        ensure!(
            verifying_key.verify(function.name(), &inputs, additional_fee.proof()),
            "The proof for the additional fee is invalid"
        );
        Ok(())
    }
}