token.rs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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. use rand::rngs::OsRng;
  19. use rusqlite::types::Value;
  20. use darkfi::{
  21. tx::{ContractCallLeaf, Transaction, TransactionBuilder},
  22. util::parse::decode_base10,
  23. zk::{proof::ProvingKey, vm::ZkCircuit, vm_heap::empty_witnesses},
  24. zkas::ZkBinary,
  25. Error, Result,
  26. };
  27. use darkfi_money_contract::{
  28. client::{
  29. token_freeze_v1::TokenFreezeCallBuilder, token_mint_v1::TokenMintCallBuilder,
  30. MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_MINT_AUTHORITY, MONEY_TOKENS_COL_TOKEN_ID,
  31. MONEY_TOKENS_TABLE,
  32. },
  33. MoneyFunction, MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
  34. };
  35. use darkfi_sdk::{
  36. crypto::{contract_id::MONEY_CONTRACT_ID, Keypair, PublicKey, SecretKey, TokenId},
  37. pasta::pallas,
  38. tx::ContractCall,
  39. };
  40. use darkfi_serial::{deserialize, serialize, Encodable};
  41. use crate::{error::WalletDbResult, Drk};
  42. impl Drk {
  43. /// Import a token mint authority into the wallet
  44. pub async fn import_mint_authority(&self, mint_authority: SecretKey) -> WalletDbResult<()> {
  45. let token_id = TokenId::derive(mint_authority);
  46. let is_frozen = 0;
  47. let query = format!(
  48. "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
  49. MONEY_TOKENS_TABLE,
  50. MONEY_TOKENS_COL_MINT_AUTHORITY,
  51. MONEY_TOKENS_COL_TOKEN_ID,
  52. MONEY_TOKENS_COL_IS_FROZEN,
  53. );
  54. self.wallet
  55. .exec_sql(
  56. &query,
  57. rusqlite::params![serialize(&mint_authority), serialize(&token_id), is_frozen,],
  58. )
  59. .await
  60. }
  61. pub async fn list_tokens(&self) -> Result<Vec<(TokenId, SecretKey, bool)>> {
  62. let rows = match self.wallet.query_multiple(MONEY_TOKENS_TABLE, &[], &[]).await {
  63. Ok(r) => r,
  64. Err(e) => {
  65. return Err(Error::RusqliteError(format!(
  66. "[list_tokens] Tokens retrieval failed: {e:?}"
  67. )))
  68. }
  69. };
  70. let mut ret = Vec::with_capacity(rows.len());
  71. for row in rows {
  72. let Value::Blob(ref auth_bytes) = row[0] else {
  73. return Err(Error::ParseFailed("[list_tokens] Mint authority bytes parsing failed"))
  74. };
  75. let mint_authority = deserialize(auth_bytes)?;
  76. let Value::Blob(ref token_bytes) = row[1] else {
  77. return Err(Error::ParseFailed("[list_tokens] Token ID bytes parsing failed"))
  78. };
  79. let token_id = deserialize(token_bytes)?;
  80. let Value::Integer(frozen) = row[2] else {
  81. return Err(Error::ParseFailed("[list_tokens] Is frozen parsing failed"))
  82. };
  83. let Ok(frozen) = i32::try_from(frozen) else {
  84. return Err(Error::ParseFailed("[list_tokens] Is frozen parsing failed"))
  85. };
  86. ret.push((token_id, mint_authority, frozen != 0));
  87. }
  88. Ok(ret)
  89. }
  90. /// Create a token mint transaction. Returns the transaction object on success.
  91. pub async fn mint_token(
  92. &self,
  93. amount: &str,
  94. recipient: PublicKey,
  95. token_id: TokenId,
  96. ) -> Result<Transaction> {
  97. // TODO: Mint directly into DAO treasury
  98. let spend_hook = pallas::Base::zero();
  99. let user_data = pallas::Base::zero();
  100. let amount = decode_base10(amount, 8, false)?;
  101. let mut tokens = self.list_tokens().await?;
  102. tokens.retain(|x| x.0 == token_id);
  103. if tokens.is_empty() {
  104. return Err(Error::Custom(format!(
  105. "Did not find mint authority for token ID {token_id}"
  106. )))
  107. }
  108. assert!(tokens.len() == 1);
  109. let mint_authority = Keypair::new(tokens[0].1);
  110. if tokens[0].2 {
  111. return Err(Error::Custom(
  112. "This token mint is marked as frozen in the wallet".to_string(),
  113. ))
  114. }
  115. // Now we need to do a lookup for the zkas proof bincodes, and create
  116. // the circuit objects and proving keys so we can build the transaction.
  117. // We also do this through the RPC.
  118. let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
  119. let zkas_ns = MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1;
  120. let Some(token_mint_zkbin) = zkas_bins.iter().find(|x| x.0 == zkas_ns) else {
  121. return Err(Error::Custom("Token mint circuit not found".to_string()))
  122. };
  123. let token_mint_zkbin = ZkBinary::decode(&token_mint_zkbin.1)?;
  124. let token_mint_circuit =
  125. ZkCircuit::new(empty_witnesses(&token_mint_zkbin)?, &token_mint_zkbin);
  126. eprintln!("Creating token mint circuit proving keys");
  127. let token_mint_pk = ProvingKey::build(token_mint_zkbin.k, &token_mint_circuit);
  128. let mint_builder = TokenMintCallBuilder {
  129. mint_authority,
  130. recipient,
  131. amount,
  132. spend_hook,
  133. user_data,
  134. token_mint_zkbin,
  135. token_mint_pk,
  136. };
  137. eprintln!("Building transaction parameters");
  138. let debris = mint_builder.build()?;
  139. // Encode and sign the transaction
  140. let mut data = vec![MoneyFunction::TokenMintV1 as u8];
  141. debris.params.encode(&mut data)?;
  142. let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
  143. let mut tx_builder =
  144. TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
  145. let mut tx = tx_builder.build()?;
  146. let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
  147. tx.signatures = vec![sigs];
  148. Ok(tx)
  149. }
  150. /// Create a token freeze transaction. Returns the transaction object on success.
  151. pub async fn freeze_token(&self, token_id: TokenId) -> Result<Transaction> {
  152. let mut tokens = self.list_tokens().await?;
  153. tokens.retain(|x| x.0 == token_id);
  154. if tokens.is_empty() {
  155. return Err(Error::Custom(format!(
  156. "Did not find mint authority for token ID {token_id}"
  157. )))
  158. }
  159. assert!(tokens.len() == 1);
  160. let mint_authority = Keypair::new(tokens[0].1);
  161. if tokens[0].2 {
  162. return Err(Error::Custom(
  163. "This token is already marked as frozen in the wallet".to_string(),
  164. ))
  165. }
  166. let zkas_bins = self.lookup_zkas(&MONEY_CONTRACT_ID).await?;
  167. let zkas_ns = MONEY_CONTRACT_ZKAS_TOKEN_FRZ_NS_V1;
  168. let Some(token_freeze_zkbin) = zkas_bins.iter().find(|x| x.0 == zkas_ns) else {
  169. return Err(Error::Custom("Token freeze circuit not found".to_string()))
  170. };
  171. let token_freeze_zkbin = ZkBinary::decode(&token_freeze_zkbin.1)?;
  172. let token_freeze_circuit =
  173. ZkCircuit::new(empty_witnesses(&token_freeze_zkbin)?, &token_freeze_zkbin);
  174. eprintln!("Creating token freeze circuit proving keys");
  175. let token_freeze_pk = ProvingKey::build(token_freeze_zkbin.k, &token_freeze_circuit);
  176. let freeze_builder =
  177. TokenFreezeCallBuilder { mint_authority, token_freeze_zkbin, token_freeze_pk };
  178. eprintln!("Building transaction parameters");
  179. let debris = freeze_builder.build()?;
  180. // Encode and sign the transaction
  181. let mut data = vec![MoneyFunction::TokenFreezeV1 as u8];
  182. debris.params.encode(&mut data)?;
  183. let call = ContractCall { contract_id: *MONEY_CONTRACT_ID, data };
  184. let mut tx_builder =
  185. TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
  186. let mut tx = tx_builder.build()?;
  187. let sigs = tx.create_sigs(&mut OsRng, &[mint_authority.secret])?;
  188. tx.signatures = vec![sigs];
  189. Ok(tx)
  190. }
  191. }