deploy.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. impl Drk {
  46. /// Initialize wallet with tables for the Deployooor contract.
  47. pub fn initialize_deployooor(&self) -> WalletDbResult<()> {
  48. // Initialize Deployooor wallet schema
  49. let wallet_schema = include_str!("../deploy.sql");
  50. self.wallet.exec_batch_sql(wallet_schema)?;
  51. Ok(())
  52. }
  53. /// Generate a new deploy authority keypair and place it into the wallet
  54. pub async fn deploy_auth_keygen(&self) -> WalletDbResult<()> {
  55. eprintln!("Generating a new keypair");
  56. let keypair = Keypair::random(&mut OsRng);
  57. let query = format!(
  58. "INSERT INTO {} ({}, {}) VALUES (?1, ?2);",
  59. *DEPLOY_AUTH_TABLE, DEPLOY_AUTH_COL_DEPLOY_AUTHORITY, DEPLOY_AUTH_COL_IS_FROZEN,
  60. );
  61. self.wallet.exec_sql(&query, rusqlite::params![serialize_async(&keypair).await, 0])?;
  62. eprintln!("Created new contract deploy authority");
  63. println!("Contract ID: {}", ContractId::derive_public(keypair.public));
  64. Ok(())
  65. }
  66. /// List contract deploy authorities from the wallet
  67. pub async fn list_deploy_auth(&self) -> Result<Vec<(i64, ContractId, bool)>> {
  68. let rows = match self.wallet.query_multiple(&DEPLOY_AUTH_TABLE, &[], &[]) {
  69. Ok(r) => r,
  70. Err(e) => {
  71. return Err(Error::DatabaseError(format!(
  72. "[list_deploy_auth] Deploy auth retrieval failed: {e:?}",
  73. )))
  74. }
  75. };
  76. let mut ret = Vec::with_capacity(rows.len());
  77. for row in rows {
  78. let Value::Integer(idx) = row[0] else {
  79. return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse index"))
  80. };
  81. let Value::Blob(ref auth_bytes) = row[1] else {
  82. return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse keypair bytes"))
  83. };
  84. let deploy_auth: Keypair = deserialize_async(auth_bytes).await?;
  85. let Value::Integer(frozen) = row[2] else {
  86. return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse \"is_frozen\""))
  87. };
  88. ret.push((idx, ContractId::derive_public(deploy_auth.public), frozen != 0))
  89. }
  90. Ok(ret)
  91. }
  92. /// Retrieve a deploy authority keypair given an index
  93. async fn get_deploy_auth(&self, idx: u64) -> Result<Keypair> {
  94. // Find the deploy authority keypair
  95. let row = match self.wallet.query_single(
  96. &DEPLOY_AUTH_TABLE,
  97. &[DEPLOY_AUTH_COL_DEPLOY_AUTHORITY],
  98. convert_named_params! {(DEPLOY_AUTH_COL_ID, idx)},
  99. ) {
  100. Ok(v) => v,
  101. Err(e) => {
  102. return Err(Error::DatabaseError(format!(
  103. "[deploy_contract] Failed to retrieve deploy authority keypair: {e:?}"
  104. )))
  105. }
  106. };
  107. let Value::Blob(ref keypair_bytes) = row[0] else {
  108. return Err(Error::ParseFailed("[deploy_contract] Failed to parse keypair bytes"))
  109. };
  110. let keypair: Keypair = deserialize_async(keypair_bytes).await?;
  111. Ok(keypair)
  112. }
  113. /// Create a feeless contract deployment transaction.
  114. pub async fn deploy_contract(
  115. &self,
  116. deploy_auth: u64,
  117. wasm_bincode: Vec<u8>,
  118. deploy_ix: Vec<u8>,
  119. ) -> Result<Transaction> {
  120. // Fetch the keypair
  121. let deploy_keypair = self.get_deploy_auth(deploy_auth).await?;
  122. // Create the contract call
  123. let deploy_call = DeployCallBuilder { deploy_keypair, wasm_bincode, deploy_ix };
  124. let deploy_debris = deploy_call.build()?;
  125. // Encode the call
  126. let mut data = vec![DeployFunction::DeployV1 as u8];
  127. deploy_debris.params.encode_async(&mut data).await?;
  128. let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
  129. let mut tx_builder =
  130. TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
  131. let mut tx = tx_builder.build()?;
  132. let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
  133. tx.signatures = vec![sigs];
  134. Ok(tx)
  135. }
  136. /// Create a feeless contract redeployment lock transaction.
  137. pub async fn lock_contract(&self, deploy_auth: u64) -> Result<Transaction> {
  138. // Fetch the keypair
  139. let deploy_keypair = self.get_deploy_auth(deploy_auth).await?;
  140. // Create the contract call
  141. let lock_call = LockCallBuilder { deploy_keypair };
  142. let lock_debris = lock_call.build()?;
  143. // Encode the call
  144. let mut data = vec![DeployFunction::LockV1 as u8];
  145. lock_debris.params.encode_async(&mut data).await?;
  146. let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
  147. let mut tx_builder =
  148. TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
  149. let mut tx = tx_builder.build()?;
  150. let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
  151. tx.signatures = vec![sigs];
  152. Ok(tx)
  153. }
  154. }