/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2023 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use anyhow::{anyhow, Result}; use darkfi::{ tx::Transaction, util::parse::decode_base10, zk::{proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses}, zkas::ZkBinary, }; use darkfi_money_contract::{ client::{token_freeze_v1::TokenFreezeCallBuilder, token_mint_v1::TokenMintCallBuilder}, MoneyFunction, MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1, }; use darkfi_sdk::{ crypto::{contract_id::MONEY_CONTRACT_ID, Keypair, PublicKey, TokenId}, pasta::pallas, tx::ContractCall, }; use darkfi_serial::Encodable; use rand::rngs::OsRng; use super::Drk; impl Drk { /// Create a token mint transaction. Returns the transaction object on success. pub async fn mint_token( &self, amount: &str, recipient: PublicKey, token_id: TokenId, ) -> Result { // TODO: Mint directly into DAO treasury let spend_hook = pallas::Base::zero(); let user_data = pallas::Base::zero(); let amount = decode_base10(amount, 8, false)?; let mut tokens = self.list_tokens().await?; tokens.retain(|x| x.0 == token_id); if tokens.is_empty() { return Err(anyhow!("Did not find mint authority for token ID {}", token_id)) } assert!(tokens.len() == 1); let mint_authority = Keypair::new(tokens[0].1); if tokens[0].2 { return Err(anyhow!("This token mint is marked as frozen in the wallet")) } // Now we need to do a lookup for the zkas proof bincodes, and create // the circuit objects and proving keys so we can build the transaction. // We also do this through the RPC. let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?; let zkas_ns = MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1; let Some(token_mint_zkbin) = zkas_bins.iter().find(|x| x.0 == zkas_ns) else { return Err(anyhow!("Token mint circuit not found")) }; let token_mint_zkbin = ZkBinary::decode(&token_mint_zkbin.1)?; let token_mint_circuit = ZkCircuit::new(empty_witnesses(&token_mint_zkbin), &token_mint_zkbin); eprintln!("Creating token mint circuit proving keys"); let token_mint_pk = ProvingKey::build(token_mint_zkbin.k, &token_mint_circuit); let mint_builder = TokenMintCallBuilder { mint_authority, recipient, amount, spend_hook, user_data, token_mint_zkbin, token_mint_pk, }; eprintln!("Building transaction parameters"); let debris = mint_builder.build()?; // Encode and sign the transaction let mut data = vec![MoneyFunction::TokenMintV1 as u8]; debris.params.encode(&mut data)?; let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }]; let proofs = vec![debris.proofs]; let mut tx = Transaction { calls, proofs, signatures: vec![] }; let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?; tx.signatures = vec![sigs]; Ok(tx) } /// Create a token freeze transaction. Returns the transaction object on success. pub async fn freeze_token(&self, token_id: TokenId) -> Result { let mut tokens = self.list_tokens().await?; tokens.retain(|x| x.0 == token_id); if tokens.is_empty() { return Err(anyhow!("Did not find mint authority for token ID {}", token_id)) } assert!(tokens.len() == 1); let mint_authority = Keypair::new(tokens[0].1); if tokens[0].2 { return Err(anyhow!("This token is already marked as frozen in the wallet")) } let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?; let zkas_ns = MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1; let Some(token_freeze_zkbin) = zkas_bins.iter().find(|x| x.0 == zkas_ns) else { return Err(anyhow!("Token freeze circuit not found")) }; let token_freeze_zkbin = ZkBinary::decode(&token_freeze_zkbin.1)?; let token_freeze_circuit = ZkCircuit::new(empty_witnesses(&token_freeze_zkbin), &token_freeze_zkbin); eprintln!("Creating token freeze circuit proving keys"); let token_freeze_pk = ProvingKey::build(token_freeze_zkbin.k, &token_freeze_circuit); let freeze_builder = TokenFreezeCallBuilder { mint_authority, token_freeze_zkbin, token_freeze_pk }; eprintln!("Building transaction parameters"); let debris = freeze_builder.build()?; // Encode and sign the transaction let mut data = vec![MoneyFunction::TokenFreezeV1 as u8]; debris.params.encode(&mut data)?; let calls = vec![ContractCall { contract_id: *MONEY_CONTRACT_ID, data }]; let proofs = vec![debris.proofs]; let mut tx = Transaction { calls, proofs, signatures: vec![] }; let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?; tx.signatures = vec![sigs]; Ok(tx) } }