| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 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 <https://www.gnu.org/licenses/>.
- */
- use lazy_static::lazy_static;
- use rand::rngs::OsRng;
- use darkfi::{
- tx::{ContractCallLeaf, Transaction, TransactionBuilder},
- Error, Result,
- };
- use darkfi_deployooor_contract::{
- client::{deploy_v1::DeployCallBuilder, lock_v1::LockCallBuilder},
- DeployFunction,
- };
- use darkfi_sdk::{
- crypto::{ContractId, Keypair, DEPLOYOOOR_CONTRACT_ID},
- ContractCall,
- };
- use darkfi_serial::{deserialize_async, serialize_async, AsyncEncodable};
- use rusqlite::types::Value;
- use crate::{convert_named_params, error::WalletDbResult, Drk};
- // Wallet SQL table constant names. These have to represent the `wallet.sql`
- // SQL schema. Table names are prefixed with the contract ID to avoid collisions.
- lazy_static! {
- pub static ref DEPLOY_AUTH_TABLE: String =
- format!("{}_deploy_auth", DEPLOYOOOR_CONTRACT_ID.to_string());
- }
- // DEPLOY_AUTH_TABLE
- pub const DEPLOY_AUTH_COL_ID: &str = "id";
- pub const DEPLOY_AUTH_COL_DEPLOY_AUTHORITY: &str = "deploy_authority";
- pub const DEPLOY_AUTH_COL_IS_FROZEN: &str = "is_frozen";
- pub const DEPLOY_AUTH_COL_FREEZE_HEIGHT: &str = "freeze_height";
- impl Drk {
- /// Initialize wallet with tables for the Deployooor contract.
- pub fn initialize_deployooor(&self) -> WalletDbResult<()> {
- // Initialize Deployooor wallet schema
- let wallet_schema = include_str!("../deploy.sql");
- self.wallet.exec_batch_sql(wallet_schema)?;
- Ok(())
- }
- /// Generate a new deploy authority keypair and place it into the wallet
- pub async fn deploy_auth_keygen(&self) -> WalletDbResult<()> {
- eprintln!("Generating a new keypair");
- let keypair = Keypair::random(&mut OsRng);
- let freeze_height: Option<u32> = None;
- let query = format!(
- "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
- *DEPLOY_AUTH_TABLE,
- DEPLOY_AUTH_COL_DEPLOY_AUTHORITY,
- DEPLOY_AUTH_COL_IS_FROZEN,
- DEPLOY_AUTH_COL_FREEZE_HEIGHT,
- );
- self.wallet.exec_sql(
- &query,
- rusqlite::params![serialize_async(&keypair).await, 0, freeze_height],
- )?;
- eprintln!("Created new contract deploy authority");
- println!("Contract ID: {}", ContractId::derive_public(keypair.public));
- Ok(())
- }
- /// List contract deploy authorities from the wallet
- pub async fn list_deploy_auth(&self) -> Result<Vec<(i64, ContractId, bool, Option<u32>)>> {
- let rows = match self.wallet.query_multiple(&DEPLOY_AUTH_TABLE, &[], &[]) {
- Ok(r) => r,
- Err(e) => {
- return Err(Error::DatabaseError(format!(
- "[list_deploy_auth] Deploy auth retrieval failed: {e:?}",
- )))
- }
- };
- let mut ret = Vec::with_capacity(rows.len());
- for row in rows {
- let Value::Integer(idx) = row[0] else {
- return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse index"))
- };
- let Value::Blob(ref auth_bytes) = row[1] else {
- return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse keypair bytes"))
- };
- let deploy_auth: Keypair = deserialize_async(auth_bytes).await?;
- let Value::Integer(frozen) = row[2] else {
- return Err(Error::ParseFailed("[list_deploy_auth] Failed to parse \"is_frozen\""))
- };
- let freeze_height = match row[3] {
- Value::Integer(freeze_height) => {
- let Ok(freeze_height) = u32::try_from(freeze_height) else {
- return Err(Error::ParseFailed(
- "[list_deploy_auth] Freeze height parsing failed",
- ))
- };
- Some(freeze_height)
- }
- Value::Null => None,
- _ => {
- return Err(Error::ParseFailed(
- "[list_deploy_auth] Freeze height parsing failed",
- ))
- }
- };
- ret.push((
- idx,
- ContractId::derive_public(deploy_auth.public),
- frozen != 0,
- freeze_height,
- ))
- }
- Ok(ret)
- }
- /// Retrieve a deploy authority keypair given an index
- async fn get_deploy_auth(&self, idx: u64) -> Result<Keypair> {
- // Find the deploy authority keypair
- let row = match self.wallet.query_single(
- &DEPLOY_AUTH_TABLE,
- &[DEPLOY_AUTH_COL_DEPLOY_AUTHORITY],
- convert_named_params! {(DEPLOY_AUTH_COL_ID, idx)},
- ) {
- Ok(v) => v,
- Err(e) => {
- return Err(Error::DatabaseError(format!(
- "[deploy_contract] Failed to retrieve deploy authority keypair: {e:?}"
- )))
- }
- };
- let Value::Blob(ref keypair_bytes) = row[0] else {
- return Err(Error::ParseFailed("[deploy_contract] Failed to parse keypair bytes"))
- };
- let keypair: Keypair = deserialize_async(keypair_bytes).await?;
- Ok(keypair)
- }
- /// Create a feeless contract deployment transaction.
- pub async fn deploy_contract(
- &self,
- deploy_auth: u64,
- wasm_bincode: Vec<u8>,
- deploy_ix: Vec<u8>,
- ) -> Result<Transaction> {
- // Fetch the keypair
- let deploy_keypair = self.get_deploy_auth(deploy_auth).await?;
- // Create the contract call
- let deploy_call = DeployCallBuilder { deploy_keypair, wasm_bincode, deploy_ix };
- let deploy_debris = deploy_call.build()?;
- // Encode the call
- let mut data = vec![DeployFunction::DeployV1 as u8];
- deploy_debris.params.encode_async(&mut data).await?;
- let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
- let mut tx_builder =
- TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
- let mut tx = tx_builder.build()?;
- let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
- tx.signatures = vec![sigs];
- Ok(tx)
- }
- /// Create a feeless contract redeployment lock transaction.
- pub async fn lock_contract(&self, deploy_auth: u64) -> Result<Transaction> {
- // Fetch the keypair
- let deploy_keypair = self.get_deploy_auth(deploy_auth).await?;
- // Create the contract call
- let lock_call = LockCallBuilder { deploy_keypair };
- let lock_debris = lock_call.build()?;
- // Encode the call
- let mut data = vec![DeployFunction::LockV1 as u8];
- lock_debris.params.encode_async(&mut data).await?;
- let call = ContractCall { contract_id: *DEPLOYOOOR_CONTRACT_ID, data };
- let mut tx_builder =
- TransactionBuilder::new(ContractCallLeaf { call, proofs: vec![] }, vec![])?;
- let mut tx = tx_builder.build()?;
- let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
- tx.signatures = vec![sigs];
- Ok(tx)
- }
- }
|