Pārlūkot izejas kodu

drk/deploy: Implement contract deployment transaction builder

parazyd 2 gadi atpakaļ
vecāks
revīzija
6a0e5b1311
5 mainītis faili ar 101 papildinājumiem un 8 dzēšanām
  1. 1 0
      Cargo.lock
  2. 1 0
      bin/drk/Cargo.toml
  3. 2 1
      bin/drk/deploy.sql
  4. 70 4
      bin/drk/src/deploy.rs
  5. 27 3
      bin/drk/src/main.rs

+ 1 - 0
Cargo.lock

@@ -2661,6 +2661,7 @@ dependencies = [
  "darkfi-sdk",
  "darkfi-serial",
  "darkfi_dao_contract",
+ "darkfi_deployooor_contract",
  "darkfi_money_contract",
  "easy-parallel",
  "lazy_static",

+ 1 - 0
bin/drk/Cargo.toml

@@ -13,6 +13,7 @@ edition = "2021"
 darkfi = {path = "../../", features = ["async-daemonize", "bs58", "rpc", "rusqlite"]}
 darkfi_money_contract = {path = "../../src/contract/money", features = ["no-entrypoint", "client"]}
 darkfi_dao_contract = {path = "../../src/contract/dao", features = ["no-entrypoint", "client"]}
+darkfi_deployooor_contract = {path = "../../src/contract/deployooor", features = ["no-entrypoint", "client"]}
 darkfi-sdk = {path = "../../src/sdk", features = ["async"]}
 darkfi-serial = {path = "../../src/serial"}
 

+ 2 - 1
bin/drk/deploy.sql

@@ -2,6 +2,7 @@
 -- Native Contract ID: EJs7oEjKkvCeEVCmpRsd6fEoTGCFJ7WKUBfmAjwaegN
 
 CREATE TABLE IF NOT EXISTS EJs7oEjKkvCeEVCmpRsd6fEoTGCFJ7WKUBfmAjwaegN_deploy_auth (
-	deploy_authority BLOB PRIMARY KEY NOT NULL,
+	id INTEGER PRIMARY KEY AUTOINCREMENT,
+	deploy_authority BLOB UNIQUE NOT NULL,
 	is_frozen INTEGER NOT NULL
 );

+ 70 - 4
bin/drk/src/deploy.rs

@@ -19,12 +19,19 @@
 use lazy_static::lazy_static;
 use rand::rngs::OsRng;
 
-use darkfi::{Error, Result};
-use darkfi_sdk::crypto::{ContractId, Keypair, DEPLOYOOOR_CONTRACT_ID};
-use darkfi_serial::{deserialize_async, serialize_async};
+use darkfi::{
+    tx::{ContractCallLeaf, Transaction, TransactionBuilder},
+    Error, Result,
+};
+use darkfi_deployooor_contract::{client::deploy_v1::DeployCallBuilder, 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::{error::WalletDbResult, Drk};
+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.
@@ -34,6 +41,7 @@ lazy_static! {
 }
 
 // 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";
 
@@ -92,4 +100,62 @@ impl Drk {
 
         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)},
+            )
+            .await
+        {
+            Ok(v) => v,
+            Err(e) => {
+                return Err(Error::RusqliteError(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 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![])?;
+
+        // TODO: Tx fees
+
+        let mut tx = tx_builder.build()?;
+        let sigs = tx.create_sigs(&[deploy_keypair.secret])?;
+        tx.signatures = vec![sigs];
+
+        Ok(tx)
+    }
 }

+ 27 - 3
bin/drk/src/main.rs

@@ -490,16 +490,18 @@ enum ContractSubcmd {
 
     /// List deploy authorities in the wallet
     List,
-    /*
+
     /// Deploy a smart contract
     Deploy {
-        /// Path to deploy authority
+        /// Contract ID (deploy authority)
         deploy_auth: String,
 
         /// Path to contract wasm bincode
         wasm_path: String,
+
+        /// Path to serialized deploy instruction
+        deploy_ix: String,
     },
-    */
 }
 
 /// CLI-util structure
@@ -1694,6 +1696,28 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
 
                 Ok(())
             }
+
+            ContractSubcmd::Deploy { deploy_auth, wasm_path, deploy_ix } => {
+                // Read the wasm bincode and deploy instruction
+                let wasm_bin = smol::fs::read(expand_path(&wasm_path)?).await?;
+                let deploy_ix = smol::fs::read(expand_path(&deploy_ix)?).await?;
+
+                let drk =
+                    Drk::new(args.wallet_path, args.wallet_pass, Some(args.endpoint), ex).await?;
+
+                let deploy_auth = u64::from_str(&deploy_auth)?;
+
+                let tx = match drk.deploy_contract(deploy_auth, wasm_bin, deploy_ix).await {
+                    Ok(v) => v,
+                    Err(e) => {
+                        eprintln!("Error creating contract deployment tx: {}", e);
+                        exit(1);
+                    }
+                };
+
+                println!("{}", base64::encode(&serialize_async(&tx).await));
+                Ok(())
+            }
         },
     }
 }