1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
1819use darkfi::{
20 zk::{Proof, ProvingKey},
21 zkas::ZkBinary,
22Result,
23};
24use darkfi_sdk::{
25 blockchain::expected_reward,
26 crypto::{note::AeadEncryptedNote, pasta_prelude::*, Blind, FuncId, PublicKey},
27 pasta::pallas,
28};
29use log::debug;
30use rand::rngs::OsRng;
3132use crate::{
33 client::{
34 transfer_v1::{proof::create_transfer_mint_proof, TransferCallOutput},
35 MoneyNote,
36 },
37 model::{ClearInput, Coin, MoneyPoWRewardParamsV1, Output, DARK_TOKEN_ID},
38};
3940pub struct PoWRewardCallDebris {
41pub params: MoneyPoWRewardParamsV1,
42pub proofs: Vec<Proof>,
43}
4445pub struct PoWRewardRevealed {
46pub coin: Coin,
47pub value_commit: pallas::Point,
48pub token_commit: pallas::Base,
49}
5051impl PoWRewardRevealed {
52pub fn to_vec(&self) -> Vec<pallas::Base> {
53let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
5455// NOTE: It's important to keep these in the same order
56 // as the `constrain_instance` calls in the zkas code.
57vec![self.coin.inner(), *valcom_coords.x(), *valcom_coords.y(), self.token_commit]
58 }
59}
6061/// Struct holding necessary information to build a `Money::PoWRewardV1` contract call.
62pub struct PoWRewardCallBuilder {
63/// Caller's public key, corresponding to the one used in the signature
64pub signature_public: PublicKey,
65/// Rewarded block height
66pub block_height: u32,
67/// Rewarded block transactions paid fees
68pub fees: u64,
69/// Optional recipient's public key, in case we want to mint to a different address
70pub recipient: Option<PublicKey>,
71/// Optional contract spend hook to use in the output
72pub spend_hook: Option<FuncId>,
73/// Optional user data to use in the output
74pub user_data: Option<pallas::Base>,
75/// `Mint_V1` zkas circuit ZkBinary
76pub mint_zkbin: ZkBinary,
77/// Proving key for the `Mint_V1` zk circuit
78pub mint_pk: ProvingKey,
79}
8081impl PoWRewardCallBuilder {
82fn _build(&self, value: u64) -> Result<PoWRewardCallDebris> {
83debug!(target: "contract::money::client::pow_reward", "Building Money::PowRewardV1 contract call");
8485// In this call, we will build one clear input and one anonymous output.
86 // Only DARK_TOKEN_ID can be minted as PoW reward.
87let token_id = *DARK_TOKEN_ID;
8889// Building the clear input using random blinds
90let value_blind = Blind::random(&mut OsRng);
91let token_blind = Blind::random(&mut OsRng);
92let coin_blind = Blind::random(&mut OsRng);
93let c_input = ClearInput {
94 value,
95 token_id,
96 value_blind,
97 token_blind,
98 signature_public: self.signature_public,
99 };
100101// Grab the spend hook and user data to use in the output
102let spend_hook = self.spend_hook.unwrap_or(FuncId::none());
103let user_data = self.user_data.unwrap_or(pallas::Base::ZERO);
104105// Building the anonymous output
106let output = TransferCallOutput {
107 public_key: self.recipient.unwrap_or(self.signature_public),
108 value,
109 token_id,
110 spend_hook,
111 user_data,
112 blind: Blind::random(&mut OsRng),
113 };
114115debug!(target: "contract::money::client::pow_reward", "Creating token mint proof for output");
116let (proof, public_inputs) = create_transfer_mint_proof(
117&self.mint_zkbin,
118&self.mint_pk,
119&output,
120 value_blind,
121 token_blind,
122 spend_hook,
123 user_data,
124 coin_blind,
125 )?;
126127let note = MoneyNote {
128 value: output.value,
129 token_id: output.token_id,
130 spend_hook,
131 user_data,
132 coin_blind,
133 value_blind,
134 token_blind,
135 memo: vec![],
136 };
137138let encrypted_note = AeadEncryptedNote::encrypt(¬e, &output.public_key, &mut OsRng)?;
139140let c_output = Output {
141 value_commit: public_inputs.value_commit,
142 token_commit: public_inputs.token_commit,
143 coin: public_inputs.coin,
144 note: encrypted_note,
145 };
146147let params = MoneyPoWRewardParamsV1 { input: c_input, output: c_output };
148let debris = PoWRewardCallDebris { params, proofs: vec![proof] };
149Ok(debris)
150 }
151152pub fn build(&self) -> Result<PoWRewardCallDebris> {
153let reward = expected_reward(self.block_height) + self.fees;
154self._build(reward)
155 }
156157/// This function should only be used for testing, as PoW reward values are predefined
158pub fn build_with_custom_reward(&self, reward: u64) -> Result<PoWRewardCallDebris> {
159self._build(reward + self.fees)
160 }
161}