deploy.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. use lazy_static::lazy_static;
  19. use rand::rngs::OsRng;
  20. use darkfi::{
  21. tx::{ContractCallLeaf, Transaction, TransactionBuilder},
  22. Error, Result,
  23. };
  24. use darkfi_deployooor_contract::{
  25. client::{deploy_v1::DeployCallBuilder, lock_v1::LockCallBuilder},
  26. DeployFunction,
  27. };
  28. use darkfi_sdk::{
  29. crypto::{ContractId, Keypair, DEPLOYOOOR_CONTRACT_ID},
  30. ContractCall,
  31. };
  32. use darkfi_serial::{deserialize_async, serialize_async, AsyncEncodable};
  33. use rusqlite::types::Value;
  34. use crate::{convert_named_params, error::WalletDbResult, Drk};
  35. // Wallet SQL table constant names. These have to represent the `wallet.sql`
  36. // SQL schema. Table names are prefixed with the contract ID to avoid collisions.
  37. lazy_static! {
  38. pub static ref DEPLOY_AUTH_TABLE: String =
  39. format!("{}_deploy_auth", DEPLOYOOOR_CONTRACT_ID.to_string());
  40. }
  41. // DEPLOY_AUTH_TABLE
  42. pub const DEPLOY_AUTH_COL_ID: &str = "id";
  43. pub const DEPLOY_AUTH_COL_DEPLOY_AUTHORITY: &str = "deploy_authority";
  44. pub const DEPLOY_AUTH_COL_IS_FROZEN: &str = "is_frozen";
  45. pub const DEPLOY_AUTH_COL_FREEZE_HEIGHT: &str = "freeze_height";
  46. impl Drk {
  47. /// Initialize wallet with tables for the Deployooor contract.
  48. pub fn initialize_deployooor(&self) -> WalletDbResult<()> {
  49. // Initialize Deployooor wallet schema
  50. let wallet_schema = include_str!("../deploy.sql");
  51. self.wallet.exec_batch_sql(wallet_schema)?;
  52. Ok(())
  53. }
  54. /// Generate a new deploy authority keypair and place it into the wallet
  55. pub async fn deploy_auth_keygen(&self) -> WalletDbResult<()> {
  56. eprintln!("Generating a new keypair");
  57. let keypair = Keypair::random(&mut OsRng);
  58. let freeze_height: Option<u32> = None;
  59. let query = format!(
  60. "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
  61. *DEPLOY_AUTH_TABLE,
  62. DEPLOY_AUTH_COL_DEPLOY_AUTHORITY,
  63. DEPLOY_AUTH_COL_IS_FROZEN,
  64. DEPLOY_AUTH_COL_FREEZE_HEIGHT,
  65. );
  66. self.wallet.exec_sql(
  67. &query,
  68. rusqlite::params![serialize_async(&keypair).await, 0, freeze_height],
  69. )?;
  70. eprintln!("Created new contract deploy authority");
  71. println!("Contract ID: {}", ContractId::derive_public(keypair.public));
  72. Ok(())
  73. }
  74. /// List contract deploy authorities from the wallet
  75. pub async fn list_deploy_auth(&self) -> Result<Vec<(i64, ContractId, bool, Option<u32>)>> {
  76. let rows = match self.wallet.query_multiple(&DEPLOY_AUTH_TABLE, &[], &[]) {
  77. Ok(r) => r,
  78. Err(e) => {
  79. return Err(Error::DatabaseError(format!(
  80. "[list_deploy_auth] Deploy auth retrieval failed: {e:?}",
  81. )))
  82. }
  83. };
  84. let mut ret = Vec::with_capacity(rows.len());
  85. for row in rows {
  86. let Value::Integer(idx) = row[0] else {
  87. return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse index"))
  88. };
  89. let Value::Blob(ref auth_bytes) = row[1] else {
  90. return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse keypair bytes"))
  91. };
  92. let deploy_auth: Keypair = deserialize_async(auth_bytes).await?;
  93. let Value::Integer(frozen) = row[2] else {
  94. return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse \"is_frozen\""))
  95. };
  96. let freeze_height = match row[3] {
  97. Value::Integer(freeze_height) => {
  98. let Ok(freeze_height) = u32::try_from(freeze_height) else {
  99. return Err(Error::ParseFailed(
  100. "[list_deploy_auth] Freeze height parsing failed",
  101. ))
  102. };
  103. Some(freeze_height)
  104. }
  105. Value::Null => None,
  106. _ => {
  107. return Err(Error::ParseFailed(
  108. "[list_deploy_auth] Freeze height parsing failed",
  109. ))
  110. }
  111. };
  112. ret.push((
  113. idx,
  114. ContractId::derive_public(deploy_auth.public),
  115. frozen != 0,
  116. freeze_height,
  117. ))
  118. }
  119. Ok(ret)
  120. }
  121. /// Retrieve a deploy authority keypair given an index
  122. async fn get_deploy_auth(&self, idx: u64) -> Result<Keypair> {
  123. // Find the deploy authority keypair
  124. let row = match self.wallet.query_single(
  125. &DEPLOY_AUTH_TABLE,
  126. &[DEPLOY_AUTH_COL_DEPLOY_AUTHORITY],
  127. convert_named_params! {(DEPLOY_AUTH_COL_ID, idx)},
  128. ) {
  129. Ok(v) => v,
  130. Err(e) => {
  131. return Err(Error::DatabaseError(format!(
  132. "[deploy_contract] Failed to retrieve deploy authority keypair: {e:?}"
  133. )))
  134. }
  135. };
  136. let Value::Blob(ref keypair_bytes) = row[0] else {
  137. return Err(Error::ParseFailed("[deploy_contract] Failed to parse keypair bytes"))
  138. };
  139. let keypair: Keypair = deserialize_async(keypair_bytes).await?;
  140. Ok(keypair)
  141. }
  142. /// Create a feeless contract deployment transaction.
  143. pub async fn deploy_contract(
  144. &self,
  145. deploy_auth: u64,
  146. wasm_bincode: Vec<u8>,
  147. deploy_ix: Vec<u8>,
  148. ) -> Result<Transaction> {
  149. // Fetch the keypair
  150. let deploy_keypair = self.get_deploy_auth(deploy_auth).await?;
  151. // Create the contract call
  152. let deploy_call = DeployCallBuilder { deploy_keypair, wasm_bincode, deploy_ix };
  153. let deploy_debris = deploy_call.build()?;
  154. // Encode the call
  155. let mut data = vec![DeployFunction::DeployV1 as u8];
  156. deploy_debris.params.encode_async(&mut data).await?;
  157. let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
  158. let mut tx_builder =
  159. TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
  160. let mut tx = tx_builder.build()?;
  161. let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
  162. tx.signatures = vec![sigs];
  163. Ok(tx)
  164. }
  165. /// Create a feeless contract redeployment lock transaction.
  166. pub async fn lock_contract(&self, deploy_auth: u64) -> Result<Transaction> {
  167. // Fetch the keypair
  168. let deploy_keypair = self.get_deploy_auth(deploy_auth).await?;
  169. // Create the contract call
  170. let lock_call = LockCallBuilder { deploy_keypair };
  171. let lock_debris = lock_call.build()?;
  172. // Encode the call
  173. let mut data = vec![DeployFunction::LockV1 as u8];
  174. lock_debris.params.encode_async(&mut data).await?;
  175. let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
  176. let mut tx_builder =
  177. TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
  178. let mut tx = tx_builder.build()?;
  179. let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
  180. tx.signatures = vec![sigs];
  181. Ok(tx)
  182. }
  183. }