darkfi_money_contract/client/
burn_v1.rs1use darkfi::{
20 zk::{Proof, ProvingKey},
21 zkas::ZkBinary,
22 ClientFailed, Result,
23};
24use darkfi_sdk::crypto::{BaseBlind, Blind, MerkleTree, ScalarBlind, SecretKey};
25use rand::rngs::OsRng;
26use tracing::debug;
27
28use crate::{
29 client::{
30 transfer_v1::{proof::create_transfer_burn_proof, TransferCallInput},
31 OwnCoin,
32 },
33 error::MoneyError,
34 model::{Input, MoneyBurnParamsV1},
35};
36
37pub struct BurnCallBuilder {
40 pub inputs: Vec<TransferCallInput>,
42 pub burn_zkbin: ZkBinary,
44 pub burn_pk: ProvingKey,
46}
47
48impl BurnCallBuilder {
49 pub fn build(self) -> Result<(MoneyBurnParamsV1, BurnCallDebris)> {
50 debug!(target: "contract::money::client::burn::build", "Building Money::BurnV1 contract call");
51 if self.inputs.is_empty() {
52 return Err(ClientFailed::VerifyError(MoneyError::BurnMissingInputs.to_string()).into())
53 }
54
55 let mut params = MoneyBurnParamsV1 { inputs: vec![] };
56 let mut signature_secrets = vec![];
57 let mut proofs = vec![];
58
59 let token_blind = BaseBlind::random(&mut OsRng);
60 let mut input_value_blinds = vec![];
61
62 debug!(target: "contract::money::client::burn::build", "Building anonymous inputs");
63 for (i, input) in self.inputs.iter().enumerate() {
64 let value_blind = Blind::random(&mut OsRng);
65 input_value_blinds.push(value_blind);
66
67 let signature_secret = SecretKey::random(&mut OsRng);
68 signature_secrets.push(signature_secret);
69
70 debug!(target: "contract::money::client::burn::build", "Creating burn proof for input {i}");
71 let (proof, public_inputs) = create_transfer_burn_proof(
72 &self.burn_zkbin,
73 &self.burn_pk,
74 input,
75 value_blind,
76 token_blind,
77 signature_secret,
78 )?;
79
80 params.inputs.push(Input {
81 value_commit: public_inputs.value_commit,
82 token_commit: public_inputs.token_commit,
83 nullifier: public_inputs.nullifier,
84 merkle_root: public_inputs.merkle_root,
85 user_data_enc: public_inputs.user_data_enc,
86 signature_public: public_inputs.signature_public,
87 tx_local: false,
88 });
89
90 proofs.push(proof);
91 }
92
93 let secrets = BurnCallDebris { proofs, signature_secrets, input_value_blinds, token_blind };
94 Ok((params, secrets))
95 }
96}
97
98pub struct BurnCallDebris {
99 pub proofs: Vec<Proof>,
101 pub signature_secrets: Vec<SecretKey>,
103 pub input_value_blinds: Vec<ScalarBlind>,
105 pub token_blind: BaseBlind,
107}
108
109pub fn make_burn_call(
122 coins: Vec<OwnCoin>,
123 tree: MerkleTree,
124 burn_zkbin: ZkBinary,
125 burn_pk: ProvingKey,
126) -> Result<(MoneyBurnParamsV1, BurnCallDebris, Vec<OwnCoin>)> {
127 debug!(target: "contract::money::client::burn", "Building Money::BurnV1 contract call");
128
129 if coins.is_empty() {
130 return Err(ClientFailed::VerifyError(MoneyError::BurnMissingInputs.to_string()).into())
131 }
132
133 let token_id = coins[0].note.token_id;
135 for coin in &coins {
136 if coin.note.token_id != token_id {
137 return Err(ClientFailed::InvalidTokenId(coin.note.token_id.to_string()).into())
138 }
139 }
140
141 let mut inputs = vec![];
142 for coin in coins.iter() {
143 let input = TransferCallInput {
144 coin: coin.clone(),
145 merkle_path: tree.witness(coin.leaf_position, 0).unwrap(),
146 user_data_blind: Blind::random(&mut OsRng),
147 };
148
149 inputs.push(input);
150 }
151
152 let burn_builder = BurnCallBuilder { inputs, burn_zkbin, burn_pk };
153
154 let (params, secrets) = burn_builder.build()?;
155
156 Ok((params, secrets, coins))
157}