darkfi_contract_test_harness/
vks.rs

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 */
18
19use std::{
20    fs::File,
21    io::{Read, Write},
22    path::PathBuf,
23    process::Command,
24};
25
26use darkfi::{
27    zk::{empty_witnesses, ProvingKey, VerifyingKey, ZkCircuit},
28    zkas::ZkBinary,
29    Result,
30};
31use darkfi_dao_contract::{
32    DAO_CONTRACT_ZKAS_DAO_AUTH_MONEY_TRANSFER_ENC_COIN_NS,
33    DAO_CONTRACT_ZKAS_DAO_AUTH_MONEY_TRANSFER_NS, DAO_CONTRACT_ZKAS_DAO_EARLY_EXEC_NS,
34    DAO_CONTRACT_ZKAS_DAO_EXEC_NS, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
35    DAO_CONTRACT_ZKAS_DAO_PROPOSE_INPUT_NS, DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS,
36    DAO_CONTRACT_ZKAS_DAO_VOTE_INPUT_NS, DAO_CONTRACT_ZKAS_DAO_VOTE_MAIN_NS,
37};
38use darkfi_money_contract::{
39    MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1, MONEY_CONTRACT_ZKAS_BURN_NS_V1,
40    MONEY_CONTRACT_ZKAS_FEE_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
41    MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
42};
43use darkfi_sdk::crypto::contract_id::{
44    DAO_CONTRACT_ID, MONEY_CONTRACT_ID, SMART_CONTRACT_ZKAS_DB_NAME,
45};
46use darkfi_serial::{deserialize, serialize};
47
48use log::debug;
49use sled_overlay::sled;
50
51/// Update these if any circuits are changed.
52/// Delete the existing cachefiles, and enable debug logging, you will see the new hashes.
53const PKS_HASH: &str = "46a30a57bd14b6bc5851bbde8b011ba2e12765bba7901c5e42f511bdb68b3255";
54const VKS_HASH: &str = "4e6f5326b3acc7fd4f6525914be8076276b16c5601940d034de0617c94b1170f";
55
56/// Build a `PathBuf` to a cachefile
57fn cache_path(typ: &str) -> Result<PathBuf> {
58    let output = Command::new("git").arg("rev-parse").arg("--show-toplevel").output()?.stdout;
59    let mut path = PathBuf::from(String::from_utf8(output[..output.len() - 1].to_vec())?);
60    path.push("src");
61    path.push("contract");
62    path.push("test-harness");
63    path.push(typ);
64    Ok(path)
65}
66
67/// (Bincode, Namespace, VK)
68pub type Vks = Vec<(Vec<u8>, String, Vec<u8>)>;
69/// (Bincode, Namespace, VK)
70pub type Pks = Vec<(Vec<u8>, String, Vec<u8>)>;
71
72/// Generate or read cached PKs and VKs
73pub fn get_cached_pks_and_vks() -> Result<(Pks, Vks)> {
74    let pks_path = cache_path("pks.bin")?;
75    let vks_path = cache_path("vks.bin")?;
76
77    let mut pks = None;
78    let mut vks = None;
79
80    if pks_path.exists() {
81        debug!("Found {pks_path:?}");
82        let mut f = File::open(pks_path.clone())?;
83        let mut data = vec![];
84        f.read_to_end(&mut data)?;
85
86        let known_hash = blake3::Hash::from_hex(PKS_HASH)?;
87        let found_hash = blake3::hash(&data);
88
89        debug!("Known PKS hash: {known_hash}");
90        debug!("Found PKS hash: {found_hash}");
91
92        if known_hash == found_hash {
93            pks = Some(deserialize(&data)?)
94        }
95
96        drop(f);
97    }
98
99    if vks_path.exists() {
100        debug!("Found {vks_path:?}");
101        let mut f = File::open(vks_path.clone())?;
102        let mut data = vec![];
103        f.read_to_end(&mut data)?;
104
105        let known_hash = blake3::Hash::from_hex(VKS_HASH)?;
106        let found_hash = blake3::hash(&data);
107
108        debug!("Known VKS hash: {known_hash}");
109        debug!("Found VKS hash: {found_hash}");
110
111        if known_hash == found_hash {
112            vks = Some(deserialize(&data)?)
113        }
114
115        drop(f);
116    }
117
118    // Cache is correct, return
119    if let (Some(pks), Some(vks)) = (pks, vks) {
120        return Ok((pks, vks))
121    }
122
123    // Otherwise, build them
124    let bins = vec![
125        // Money
126        &include_bytes!("../../money/proof/fee_v1.zk.bin")[..],
127        &include_bytes!("../../money/proof/mint_v1.zk.bin")[..],
128        &include_bytes!("../../money/proof/burn_v1.zk.bin")[..],
129        &include_bytes!("../../money/proof/token_mint_v1.zk.bin")[..],
130        &include_bytes!("../../money/proof/auth_token_mint_v1.zk.bin")[..],
131        // DAO
132        &include_bytes!("../../dao/proof/mint.zk.bin")[..],
133        &include_bytes!("../../dao/proof/propose-input.zk.bin")[..],
134        &include_bytes!("../../dao/proof/propose-main.zk.bin")[..],
135        &include_bytes!("../../dao/proof/vote-input.zk.bin")[..],
136        &include_bytes!("../../dao/proof/vote-main.zk.bin")[..],
137        &include_bytes!("../../dao/proof/exec.zk.bin")[..],
138        &include_bytes!("../../dao/proof/early-exec.zk.bin")[..],
139        &include_bytes!("../../dao/proof/auth-money-transfer.zk.bin")[..],
140        &include_bytes!("../../dao/proof/auth-money-transfer-enc-coin.zk.bin")[..],
141    ];
142
143    let mut pks = vec![];
144    let mut vks = vec![];
145
146    for bincode in bins.iter() {
147        let zkbin = ZkBinary::decode(bincode)?;
148        debug!("Building PK for {}", zkbin.namespace);
149        let witnesses = empty_witnesses(&zkbin)?;
150        let circuit = ZkCircuit::new(witnesses, &zkbin);
151
152        let pk = ProvingKey::build(zkbin.k, &circuit);
153        let mut pk_buf = vec![];
154        pk.write(&mut pk_buf)?;
155        pks.push((bincode.to_vec(), zkbin.namespace.clone(), pk_buf));
156
157        debug!("Building VK for {}", zkbin.namespace);
158        let vk = VerifyingKey::build(zkbin.k, &circuit);
159        let mut vk_buf = vec![];
160        vk.write(&mut vk_buf)?;
161        vks.push((bincode.to_vec(), zkbin.namespace.clone(), vk_buf));
162    }
163
164    debug!("Writing PKs to {pks_path:?}");
165    let mut f = File::create(&pks_path)?;
166    let ser = serialize(&pks);
167    let hash = blake3::hash(&ser);
168    debug!("{pks_path:?} {hash}");
169    f.write_all(&ser)?;
170
171    debug!("Writing VKs to {vks_path:?}");
172    let mut f = File::create(&vks_path)?;
173    let ser = serialize(&vks);
174    let hash = blake3::hash(&ser);
175    debug!("{vks_path:?} {hash}");
176    f.write_all(&ser)?;
177
178    Ok((pks, vks))
179}
180
181/// Inject cached VKs into a given blockchain database reference
182pub fn inject(sled_db: &sled::Db, vks: &Vks) -> Result<()> {
183    // Derive the database names for the specific contracts
184    let money_db_name = MONEY_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME);
185    let dao_db_name = DAO_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME);
186
187    // Create the db trees
188    let money_tree = sled_db.open_tree(money_db_name)?;
189    let dao_tree = sled_db.open_tree(dao_db_name)?;
190
191    for (bincode, namespace, vk) in vks.iter() {
192        match namespace.as_str() {
193            // Money contract circuits
194            MONEY_CONTRACT_ZKAS_FEE_NS_V1 |
195            MONEY_CONTRACT_ZKAS_MINT_NS_V1 |
196            MONEY_CONTRACT_ZKAS_BURN_NS_V1 |
197            MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1 |
198            MONEY_CONTRACT_ZKAS_AUTH_TOKEN_MINT_NS_V1 => {
199                let key = serialize(&namespace.as_str());
200                let value = serialize(&(bincode.clone(), vk.clone()));
201                money_tree.insert(key, value)?;
202            }
203
204            // DAO contract circuits
205            DAO_CONTRACT_ZKAS_DAO_MINT_NS |
206            DAO_CONTRACT_ZKAS_DAO_VOTE_INPUT_NS |
207            DAO_CONTRACT_ZKAS_DAO_VOTE_MAIN_NS |
208            DAO_CONTRACT_ZKAS_DAO_PROPOSE_INPUT_NS |
209            DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS |
210            DAO_CONTRACT_ZKAS_DAO_EXEC_NS |
211            DAO_CONTRACT_ZKAS_DAO_EARLY_EXEC_NS |
212            DAO_CONTRACT_ZKAS_DAO_AUTH_MONEY_TRANSFER_NS |
213            DAO_CONTRACT_ZKAS_DAO_AUTH_MONEY_TRANSFER_ENC_COIN_NS => {
214                let key = serialize(&namespace.as_str());
215                let value = serialize(&(bincode.clone(), vk.clone()));
216                dao_tree.insert(key, value)?;
217            }
218
219            x => panic!("Found unhandled zkas namespace {x}"),
220        }
221    }
222
223    Ok(())
224}