darkfi_money_contract/client/transfer_v1/
proof.rs
1use darkfi::{
20 zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
21 zkas::ZkBinary,
22 Result,
23};
24use darkfi_sdk::{
25 bridgetree::Hashable,
26 crypto::{
27 pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, BaseBlind, FuncId, MerkleNode,
28 PublicKey, ScalarBlind, SecretKey,
29 },
30 pasta::pallas,
31};
32use log::debug;
33use rand::rngs::OsRng;
34
35use super::{TransferCallInput, TransferCallOutput};
36use crate::model::{Coin, CoinAttributes, Nullifier};
37
38pub struct TransferMintRevealed {
39 pub coin: Coin,
40 pub value_commit: pallas::Point,
41 pub token_commit: pallas::Base,
42}
43
44impl TransferMintRevealed {
45 pub fn to_vec(&self) -> Vec<pallas::Base> {
46 let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
47
48 vec![self.coin.inner(), *valcom_coords.x(), *valcom_coords.y(), self.token_commit]
51 }
52}
53
54pub struct TransferBurnRevealed {
55 pub value_commit: pallas::Point,
56 pub token_commit: pallas::Base,
57 pub nullifier: Nullifier,
58 pub merkle_root: MerkleNode,
59 pub spend_hook: FuncId,
60 pub user_data_enc: pallas::Base,
61 pub signature_public: PublicKey,
62}
63
64impl TransferBurnRevealed {
65 pub fn to_vec(&self) -> Vec<pallas::Base> {
66 let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
67
68 vec![
71 self.nullifier.inner(),
72 *valcom_coords.x(),
73 *valcom_coords.y(),
74 self.token_commit,
75 self.merkle_root.inner(),
76 self.user_data_enc,
77 self.spend_hook.inner(),
78 self.signature_public.x(),
79 self.signature_public.y(),
80 ]
81 }
82}
83
84pub fn create_transfer_burn_proof(
85 zkbin: &ZkBinary,
86 pk: &ProvingKey,
87 input: &TransferCallInput,
88 value_blind: ScalarBlind,
89 token_blind: BaseBlind,
90 signature_secret: SecretKey,
91) -> Result<(Proof, TransferBurnRevealed)> {
92 let public_key = PublicKey::from_secret(input.coin.secret);
93 let signature_public = PublicKey::from_secret(signature_secret);
94
95 let coin = CoinAttributes {
96 public_key,
97 value: input.coin.note.value,
98 token_id: input.coin.note.token_id,
99 spend_hook: input.coin.note.spend_hook,
100 user_data: input.coin.note.user_data,
101 blind: input.coin.note.coin_blind,
102 }
103 .to_coin();
104
105 let merkle_root = {
106 let position: u64 = input.coin.leaf_position.into();
107 let mut current = MerkleNode::from(coin.inner());
108 for (level, sibling) in input.merkle_path.iter().enumerate() {
109 let level = level as u8;
110 current = if position & (1 << level) == 0 {
111 MerkleNode::combine(level.into(), ¤t, sibling)
112 } else {
113 MerkleNode::combine(level.into(), sibling, ¤t)
114 };
115 }
116 current
117 };
118
119 let user_data_enc = poseidon_hash([input.coin.note.user_data, input.user_data_blind.inner()]);
120 let value_commit = pedersen_commitment_u64(input.coin.note.value, value_blind);
121 let token_commit = poseidon_hash([input.coin.note.token_id.inner(), token_blind.inner()]);
122
123 let public_inputs = TransferBurnRevealed {
124 value_commit,
125 token_commit,
126 nullifier: input.coin.nullifier(),
127 merkle_root,
128 spend_hook: input.coin.note.spend_hook,
129 user_data_enc,
130 signature_public,
131 };
132
133 let prover_witnesses = vec![
134 Witness::Base(Value::known(input.coin.secret.inner())),
135 Witness::Base(Value::known(pallas::Base::from(input.coin.note.value))),
136 Witness::Base(Value::known(input.coin.note.token_id.inner())),
137 Witness::Base(Value::known(input.coin.note.spend_hook.inner())),
138 Witness::Base(Value::known(input.coin.note.user_data)),
139 Witness::Base(Value::known(input.coin.note.coin_blind.inner())),
140 Witness::Scalar(Value::known(value_blind.inner())),
141 Witness::Base(Value::known(token_blind.inner())),
142 Witness::Base(Value::known(input.user_data_blind.inner())),
143 Witness::Uint32(Value::known(u64::from(input.coin.leaf_position).try_into().unwrap())),
144 Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
145 Witness::Base(Value::known(signature_secret.inner())),
146 ];
147
148 let circuit = ZkCircuit::new(prover_witnesses, zkbin);
150 let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
151
152 Ok((proof, public_inputs))
153}
154
155#[allow(clippy::too_many_arguments)]
156pub fn create_transfer_mint_proof(
157 zkbin: &ZkBinary,
158 pk: &ProvingKey,
159 output: &TransferCallOutput,
160 value_blind: ScalarBlind,
161 token_blind: BaseBlind,
162 spend_hook: FuncId,
163 user_data: pallas::Base,
164 coin_blind: BaseBlind,
165) -> Result<(Proof, TransferMintRevealed)> {
166 let value_commit = pedersen_commitment_u64(output.value, value_blind);
167 let token_commit = poseidon_hash([output.token_id.inner(), token_blind.inner()]);
168 let (pub_x, pub_y) = output.public_key.xy();
169
170 let coin = CoinAttributes {
171 public_key: output.public_key,
172 value: output.value,
173 token_id: output.token_id,
174 spend_hook,
175 user_data,
176 blind: coin_blind,
177 };
178 debug!(target: "contract::money::client::transfer::proof", "Created coin: {:?}", coin);
179 let coin = coin.to_coin();
180
181 let public_inputs = TransferMintRevealed { coin, value_commit, token_commit };
182
183 let prover_witnesses = vec![
184 Witness::Base(Value::known(pub_x)),
185 Witness::Base(Value::known(pub_y)),
186 Witness::Base(Value::known(pallas::Base::from(output.value))),
187 Witness::Base(Value::known(output.token_id.inner())),
188 Witness::Base(Value::known(spend_hook.inner())),
189 Witness::Base(Value::known(user_data)),
190 Witness::Base(Value::known(coin_blind.inner())),
191 Witness::Scalar(Value::known(value_blind.inner())),
192 Witness::Base(Value::known(token_blind.inner())),
193 ];
194
195 let circuit = ZkCircuit::new(prover_witnesses, zkbin);
197 let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
198
199 Ok((proof, public_inputs))
200}