darkfi/validator/
fees.rs

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
/* This file is part of DarkFi (https://dark.fi)
 *
 * Copyright (C) 2020-2024 Dyne.org foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use darkfi_sdk::crypto::constants::{MERKLE_DEPTH_ORCHARD, SPARSE_MERKLE_DEPTH};
use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};

use crate::zkas::{Opcode, VarType, ZkBinary};

/// Fixed fee for verifying Schnorr signatures using the Pallas elliptic curve
pub const PALLAS_SCHNORR_SIGNATURE_FEE: u64 = 1000;

/// Calculate the gas use for verifying a given zkas circuit.
/// This function assumes that the zkbin was properly decoded.
pub fn circuit_gas_use(zkbin: &ZkBinary) -> u64 {
    let mut accumulator: u64 = 0;

    // Constants each with a cost of 10
    accumulator += 10 * zkbin.constants.len() as u64;

    // Literals each with a cost of 10 (for now there's only 1 type of literal)
    accumulator += 10 * zkbin.literals.len() as u64;

    // Witnesses have cost by type
    for witness in &zkbin.witnesses {
        let cost = match witness {
            VarType::Dummy => unreachable!(),
            VarType::EcPoint => 20,
            VarType::EcFixedPoint => unreachable!(),
            VarType::EcFixedPointShort => unreachable!(),
            VarType::EcFixedPointBase => unreachable!(),
            VarType::EcNiPoint => 20,
            VarType::Base => 10,
            VarType::BaseArray => unreachable!(),
            VarType::Scalar => 20,
            VarType::ScalarArray => unreachable!(),
            VarType::MerklePath => 10 * MERKLE_DEPTH_ORCHARD as u64,
            VarType::SparseMerklePath => 10 * SPARSE_MERKLE_DEPTH as u64,
            VarType::Uint32 => 10,
            VarType::Uint64 => 10,
            VarType::Any => 10,
        };

        accumulator += cost;
    }

    // Opcodes depending on how heavy they are
    for opcode in &zkbin.opcodes {
        let cost = match opcode.0 {
            Opcode::Noop => unreachable!(),
            Opcode::EcAdd => 30,
            Opcode::EcMul => 30,
            Opcode::EcMulBase => 30,
            Opcode::EcMulShort => 30,
            Opcode::EcMulVarBase => 30,
            Opcode::EcGetX => 5,
            Opcode::EcGetY => 5,
            Opcode::PoseidonHash => 20 + 10 * opcode.1.len() as u64,
            Opcode::MerkleRoot => 10 * MERKLE_DEPTH_ORCHARD as u64,
            Opcode::SparseMerkleRoot => 10 * SPARSE_MERKLE_DEPTH as u64,
            Opcode::BaseAdd => 15,
            Opcode::BaseMul => 15,
            Opcode::BaseSub => 15,
            Opcode::WitnessBase => 10,
            Opcode::RangeCheck => 60,
            Opcode::LessThanStrict => 100,
            Opcode::LessThanLoose => 100,
            Opcode::BoolCheck => 20,
            Opcode::CondSelect => 10,
            Opcode::ZeroCondSelect => 10,
            Opcode::ConstrainEqualBase => 10,
            Opcode::ConstrainEqualPoint => 20,
            Opcode::ConstrainInstance => 10,
            Opcode::DebugPrint => 100,
        };

        accumulator += cost;
    }

    accumulator
}

/// Auxiliary struct representing the full gas usage breakdown of a transaction.
///
/// This data is used for accounting of fees, providing details relating to
/// resource consumption across different transactions.
#[derive(Default, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
pub struct GasData {
    /// Wasm calls gas consumption
    pub wasm: u64,
    /// ZK circuits gas consumption
    pub zk_circuits: u64,
    /// Signature fee
    pub signatures: u64,
    /// Contract deployment gas
    pub deployments: u64,
    /// Transaction paid fee
    pub paid: u64,
}

impl GasData {
    /// Calculates the total gas used by summing all individual gas usage fields.
    pub fn total_gas_used(&self) -> u64 {
        self.wasm + self.zk_circuits + self.signatures + self.deployments
    }
}

/// Implements custom debug trait to include [`GasData::total_gas_used`].
impl std::fmt::Debug for GasData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("GasData")
            .field("total", &self.total_gas_used())
            .field("wasm", &self.wasm)
            .field("zk_circuits", &self.zk_circuits)
            .field("signatures", &self.signatures)
            .field("deployments", &self.deployments)
            .field("paid", &self.paid)
            .finish()
    }
}