darkfi_money_contract/client/
genesis_mint_v1.rs1use darkfi::{
20 zk::{Proof, ProvingKey},
21 zkas::ZkBinary,
22 ClientFailed, Result,
23};
24use darkfi_sdk::{
25 crypto::{note::AeadEncryptedNote, pasta_prelude::*, Blind, FuncId, PublicKey},
26 pasta::pallas,
27};
28use log::debug;
29use rand::{prelude::SliceRandom, rngs::OsRng};
30
31use crate::{
32 client::{
33 compute_remainder_blind,
34 transfer_v1::{proof::create_transfer_mint_proof, TransferCallOutput},
35 MoneyNote,
36 },
37 model::{ClearInput, Coin, MoneyGenesisMintParamsV1, Output, DARK_TOKEN_ID},
38};
39
40pub struct GenesisMintCallDebris {
41 pub params: MoneyGenesisMintParamsV1,
42 pub proofs: Vec<Proof>,
43}
44
45pub struct GenesisMintRevealed {
46 pub coin: Coin,
47 pub value_commit: pallas::Point,
48 pub token_commit: pallas::Base,
49}
50
51impl GenesisMintRevealed {
52 pub fn to_vec(&self) -> Vec<pallas::Base> {
53 let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
54
55 vec![self.coin.inner(), *valcom_coords.x(), *valcom_coords.y(), self.token_commit]
58 }
59}
60
61pub struct GenesisMintCallBuilder {
63 pub signature_public: PublicKey,
65 pub amounts: Vec<u64>,
67 pub recipient: Option<PublicKey>,
69 pub spend_hook: Option<FuncId>,
71 pub user_data: Option<pallas::Base>,
73 pub mint_zkbin: ZkBinary,
75 pub mint_pk: ProvingKey,
77}
78
79impl GenesisMintCallBuilder {
80 pub fn build(&self) -> Result<GenesisMintCallDebris> {
81 debug!(target: "contract::money::client::genesis_mint", "Building Money::MintV1 contract call");
82 let value = self.amounts.iter().sum();
83 if value == 0 {
84 return Err(ClientFailed::InvalidAmount(value).into())
85 }
86
87 let token_id = *DARK_TOKEN_ID;
90
91 let value_blind = Blind::random(&mut OsRng);
93 let token_blind = Blind::random(&mut OsRng);
94 let input = ClearInput {
95 value,
96 token_id,
97 value_blind,
98 token_blind,
99 signature_public: self.signature_public,
100 };
101
102 let public_key = self.recipient.unwrap_or(self.signature_public);
104 let spend_hook = self.spend_hook.unwrap_or(FuncId::none());
105 let user_data = self.user_data.unwrap_or(pallas::Base::ZERO);
106
107 let mut amounts = self.amounts.clone();
109 amounts.shuffle(&mut OsRng);
110
111 let input_blinds = vec![value_blind];
113 let mut output_blinds = Vec::with_capacity(amounts.len());
114 let mut outputs = Vec::with_capacity(amounts.len());
115 let mut proofs = Vec::with_capacity(amounts.len());
116 for (i, amount) in amounts.iter().enumerate() {
117 let value_blind = if i == amounts.len() - 1 {
118 compute_remainder_blind(&input_blinds, &output_blinds)
119 } else {
120 Blind::random(&mut OsRng)
121 };
122 output_blinds.push(value_blind);
123
124 let output = TransferCallOutput {
125 public_key,
126 value: *amount,
127 token_id,
128 spend_hook,
129 user_data,
130 blind: Blind::random(&mut OsRng),
131 };
132
133 debug!(target: "contract::money::client::genesis_mint", "Creating token mint proof for output {i}");
134 let (proof, public_inputs) = create_transfer_mint_proof(
135 &self.mint_zkbin,
136 &self.mint_pk,
137 &output,
138 value_blind,
139 token_blind,
140 spend_hook,
141 user_data,
142 output.blind,
143 )?;
144 proofs.push(proof);
145
146 let note = MoneyNote {
147 value: output.value,
148 token_id: output.token_id,
149 spend_hook,
150 user_data,
151 coin_blind: output.blind,
152 value_blind,
153 token_blind,
154 memo: vec![],
155 };
156
157 let encrypted_note = AeadEncryptedNote::encrypt(¬e, &public_key, &mut OsRng)?;
158
159 let output = Output {
160 value_commit: public_inputs.value_commit,
161 token_commit: public_inputs.token_commit,
162 coin: public_inputs.coin,
163 note: encrypted_note,
164 };
165
166 outputs.push(output);
167 }
168
169 let params = MoneyGenesisMintParamsV1 { input, outputs };
170 let debris = GenesisMintCallDebris { params, proofs };
171 Ok(debris)
172 }
173}