Просмотр исходного кода

drk/interactive: contract commands added

skoupidi 1 год назад
Родитель
Сommit
ec682e19a9
4 измененных файлов с 232 добавлено и 13 удалено
  1. 26 0
      bin/drk/src/cli_util.rs
  2. 4 4
      bin/drk/src/deploy.rs
  3. 194 4
      bin/drk/src/interactive.rs
  4. 8 5
      bin/drk/src/main.rs

+ 26 - 0
bin/drk/src/cli_util.rs

@@ -484,6 +484,31 @@ pub fn generate_completions(shell: &str) -> Result<String> {
         freeze,
     ]);
 
+    // Contract
+    let generate_deploy =
+        SubCommand::with_name("generate-deploy").about("Generate a new deploy authority");
+
+    let list = SubCommand::with_name("list").about("List deploy authorities in the wallet");
+
+    let deploy_auth = Arg::with_name("deploy-auth").help("Contract ID (deploy authority)");
+
+    let wasm_path = Arg::with_name("wasm-path").help("Path to contract wasm bincode");
+
+    let deploy_ix = Arg::with_name("deploy-ix").help("Path to serialized deploy instruction");
+
+    let deploy = SubCommand::with_name("deploy").about("Deploy a smart contract").args(&vec![
+        deploy_auth.clone(),
+        wasm_path,
+        deploy_ix,
+    ]);
+
+    let lock =
+        SubCommand::with_name("lock").about("Lock a smart contract").args(&vec![deploy_auth]);
+
+    let contract = SubCommand::with_name("contract")
+        .about("Contract functionalities")
+        .subcommands(vec![generate_deploy, list, deploy, lock]);
+
     // Main arguments
     let config = Arg::with_name("config")
         .short("c")
@@ -514,6 +539,7 @@ pub fn generate_completions(shell: &str) -> Result<String> {
         explorer,
         alias,
         token,
+        contract,
     ];
 
     let fun = Arg::with_name("fun")

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

@@ -60,8 +60,8 @@ impl Drk {
     }
 
     /// 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");
+    pub async fn deploy_auth_keygen(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
+        output.push(String::from("Generating a new keypair"));
 
         let keypair = Keypair::random(&mut OsRng);
         let freeze_height: Option<u32> = None;
@@ -78,8 +78,8 @@ impl Drk {
             rusqlite::params![serialize_async(&keypair).await, 0, freeze_height],
         )?;
 
-        eprintln!("Created new contract deploy authority");
-        println!("Contract ID: {}", ContractId::derive_public(keypair.public));
+        output.push(String::from("Created new contract deploy authority"));
+        output.push(format!("Contract ID: {}", ContractId::derive_public(keypair.public)));
 
         Ok(())
     }

+ 194 - 4
bin/drk/src/interactive.rs

@@ -69,8 +69,7 @@ use crate::{
 };
 
 // TODO:
-//  1. Add rest commands handling, along with their completions, hints and help message.
-//  2. Create a transactions cache in the wallet db, so you can use it to handle them.
+//  1. Create a transactions cache in the wallet db, so you can use it to handle them.
 
 /// Auxiliary function to print the help message.
 fn help(output: &mut Vec<String>) {
@@ -104,6 +103,7 @@ fn help(output: &mut Vec<String>) {
     output.push(String::from("\texplorer: Explorer related subcommands"));
     output.push(String::from("\talias: Token alias"));
     output.push(String::from("\ttoken: Token functionalities"));
+    output.push(String::from("\tcontract: Contract functionalities"));
 }
 
 /// Auxiliary function to define the interactive shell completions.
@@ -134,7 +134,7 @@ fn completion(buffer: &str, lc: &mut Vec<String>) {
         return
     }
 
-    if last.starts_with("c") {
+    if last.starts_with("com") {
         lc.push(prefix + "completions");
         return
     }
@@ -267,6 +267,15 @@ fn completion(buffer: &str, lc: &mut Vec<String>) {
         return
     }
 
+    if last.starts_with("con") {
+        lc.push(prefix.clone() + "contract");
+        lc.push(prefix.clone() + "contract generate-deploy");
+        lc.push(prefix.clone() + "contract list");
+        lc.push(prefix.clone() + "contract deploy");
+        lc.push(prefix + "contract lock");
+        return
+    }
+
     // Now the catch alls
     if last.starts_with("a") {
         lc.push(prefix.clone() + "attach-fee");
@@ -277,6 +286,16 @@ fn completion(buffer: &str, lc: &mut Vec<String>) {
         return
     }
 
+    if last.starts_with("c") {
+        lc.push(prefix.clone() + "completions");
+        lc.push(prefix.clone() + "contract");
+        lc.push(prefix.clone() + "contract generate-deploy");
+        lc.push(prefix.clone() + "contract list");
+        lc.push(prefix.clone() + "contract deploy");
+        lc.push(prefix + "contract lock");
+        return
+    }
+
     if last.starts_with("s") {
         lc.push(prefix.clone() + "spend");
         lc.push(prefix.clone() + "subscribe");
@@ -345,6 +364,9 @@ fn hints(buffer: &str) -> Option<(String, i32, bool)> {
         "token import " => Some(("<secret-key> <token-blind>".to_string(), color, bold)),
         "token mint " => Some(("<token> <amount> <recipient> [spend-hook] [user-data]".to_string(), color, bold)),
         "token freeze " => Some(("<token>".to_string(), color, bold)),
+        "contract " => Some(("(generate-deploy|list|deploy|lock)".to_string(), color, bold)),
+        "contract deploy " => Some(("<deploy-auth> <wasm-path> <deploy-ix>".to_string(), color, bold)),
+        "contract lock " => Some(("<deploy-auth>".to_string(), color, bold)),
         _ => None,
     }
 }
@@ -533,6 +555,7 @@ pub async fn interactive(drk: &DrkPtr, endpoint: &Url, history_path: &str, ex: &
                 "explorer" => handle_explorer(drk, &parts, &input, &mut output).await,
                 "alias" => handle_alias(drk, &parts, &mut output).await,
                 "token" => handle_token(drk, &parts, &mut output).await,
+                "contract" => handle_contract(drk, &parts, &mut output).await,
                 _ => output.push(format!("Unreconized command: {}", parts[0])),
             }
 
@@ -2892,7 +2915,7 @@ async fn handle_token_list(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String
     let tokens = match lock.get_mint_authorities().await {
         Ok(m) => m,
         Err(e) => {
-            output.push(format!("Failed to fetch mint autorities: {e:?}"));
+            output.push(format!("Failed to fetch mint authorities: {e:?}"));
             return
         }
     };
@@ -3045,3 +3068,170 @@ async fn handle_token_freeze(drk: &DrkPtr, parts: &[&str], output: &mut Vec<Stri
         Err(e) => output.push(format!("Failed to create token freeze transaction: {e}")),
     }
 }
+
+/// Auxiliary function to define the contract command handling.
+async fn handle_contract(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct command structure
+    if parts.len() < 2 {
+        output.push(String::from("Malformed `contract` command"));
+        output.push(String::from("Usage: contract (generate-deploy|list|deploy|lock)"));
+        return
+    }
+
+    // Handle subcommand
+    match parts[1] {
+        "generate-deploy" => handle_contract_generate_deploy(drk, parts, output).await,
+        "list" => handle_contract_list(drk, parts, output).await,
+        "deploy" => handle_contract_deploy(drk, parts, output).await,
+        "lock" => handle_contract_lock(drk, parts, output).await,
+        _ => {
+            output.push(format!("Unreconized contract subcommand: {}", parts[1]));
+            output.push(String::from("Usage: contract (generate-deploy|list|deploy|lock)"));
+        }
+    }
+}
+
+/// Auxiliary function to define the contract generate deploy subcommand handling.
+async fn handle_contract_generate_deploy(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct subcommand structure
+    if parts.len() != 2 {
+        output.push(String::from("Malformed `contract generate-deploy` subcommand"));
+        output.push(String::from("Usage: contract generate-deploy"));
+        return
+    }
+
+    if let Err(e) = drk.read().await.deploy_auth_keygen(output).await {
+        output.push(format!("Error creating deploy auth keypair: {e}"));
+    }
+}
+
+/// Auxiliary function to define the contract list subcommand handling.
+async fn handle_contract_list(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct subcommand structure
+    if parts.len() != 2 {
+        output.push(String::from("Malformed `contract list` subcommand"));
+        output.push(String::from("Usage: contract list"));
+        return
+    }
+
+    let auths = match drk.read().await.list_deploy_auth().await {
+        Ok(a) => a,
+        Err(e) => {
+            output.push(format!("Failed to fetch deploy authorities: {e}"));
+            return
+        }
+    };
+
+    let mut table = Table::new();
+    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
+    table.set_titles(row!["Index", "Contract ID", "Frozen", "Freeze Height"]);
+
+    for (idx, contract_id, frozen, freeze_height) in auths {
+        let freeze_height = match freeze_height {
+            Some(freeze_height) => freeze_height.to_string(),
+            None => String::from("-"),
+        };
+        table.add_row(row![idx, contract_id, frozen, freeze_height]);
+    }
+
+    if table.is_empty() {
+        output.push(String::from("No deploy authorities found"));
+    } else {
+        output.push(format!("{table}"));
+    }
+}
+
+/// Auxiliary function to define the contract deploy subcommand handling.
+async fn handle_contract_deploy(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct subcommand structure
+    if parts.len() != 5 {
+        output.push(String::from("Malformed `contract deploy` subcommand"));
+        output.push(String::from("Usage: contract deploy <deploy-auth> <wasm-path> <deploy-ix>"));
+        return
+    }
+
+    let deploy_auth = match u64::from_str(parts[2]) {
+        Ok(d) => d,
+        Err(e) => {
+            output.push(format!("Invalid deploy authority: {e}"));
+            return
+        }
+    };
+
+    // Read the wasm bincode and deploy instruction
+    let file_path = match expand_path(parts[3]) {
+        Ok(p) => p,
+        Err(e) => {
+            output.push(format!("Error while expanding wasm bincode file path: {e}"));
+            return
+        }
+    };
+    let wasm_bin = match smol::fs::read(file_path).await {
+        Ok(w) => w,
+        Err(e) => {
+            output.push(format!("Error while reading wasm bincode file: {e}"));
+            return
+        }
+    };
+
+    let file_path = match expand_path(parts[4]) {
+        Ok(p) => p,
+        Err(e) => {
+            output.push(format!("Error while expanding deploy instruction file path: {e}"));
+            return
+        }
+    };
+    let deploy_ix = match smol::fs::read(file_path).await {
+        Ok(d) => d,
+        Err(e) => {
+            output.push(format!("Error while reading deploy instruction file: {e}"));
+            return
+        }
+    };
+
+    let lock = drk.read().await;
+    let mut tx = match lock.deploy_contract(deploy_auth, wasm_bin, deploy_ix).await {
+        Ok(v) => v,
+        Err(e) => {
+            output.push(format!("Error creating contract deployment tx: {e}"));
+            return
+        }
+    };
+
+    match lock.attach_fee(&mut tx).await {
+        Ok(_) => output.push(base64::encode(&serialize_async(&tx).await)),
+        Err(e) => output.push(format!("Failed to attach the fee call to the transaction: {e}")),
+    }
+}
+
+/// Auxiliary function to define the contract lock subcommand handling.
+async fn handle_contract_lock(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
+    // Check correct subcommand structure
+    if parts.len() != 3 {
+        output.push(String::from("Malformed `contract lock` subcommand"));
+        output.push(String::from("Usage: contract lock <deploy-auth>"));
+        return
+    }
+
+    let deploy_auth = match u64::from_str(parts[2]) {
+        Ok(d) => d,
+        Err(e) => {
+            output.push(format!("Invalid deploy authority: {e}"));
+            return
+        }
+    };
+
+    let lock = drk.read().await;
+    let mut tx = match lock.lock_contract(deploy_auth).await {
+        Ok(v) => v,
+        Err(e) => {
+            output.push(format!("Error creating contract lock tx: {e}"));
+            return
+        }
+    };
+
+    match lock.attach_fee(&mut tx).await {
+        Ok(_) => output.push(base64::encode(&serialize_async(&tx).await)),
+        Err(e) => output.push(format!("Failed to attach the fee call to the transaction: {e}")),
+    }
+}

+ 8 - 5
bin/drk/src/main.rs

@@ -2560,10 +2560,13 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
                 )
                 .await;
 
-                if let Err(e) = drk.deploy_auth_keygen().await {
+                let mut output = vec![];
+                if let Err(e) = drk.deploy_auth_keygen(&mut output).await {
+                    print_output(&output);
                     eprintln!("Error creating deploy auth keypair: {e}");
                     exit(2);
                 }
+                print_output(&output);
 
                 Ok(())
             }
@@ -2617,7 +2620,7 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
                 .await;
 
                 let mut tx = match drk.deploy_contract(deploy_auth, wasm_bin, deploy_ix).await {
-                    Ok(v) => v,
+                    Ok(t) => t,
                     Err(e) => {
                         eprintln!("Error creating contract deployment tx: {e}");
                         exit(2);
@@ -2625,7 +2628,7 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
                 };
 
                 if let Err(e) = drk.attach_fee(&mut tx).await {
-                    eprintln!("Failed to attach the fee call to the transaction: {e:?}");
+                    eprintln!("Failed to attach the fee call to the transaction: {e}");
                     exit(2);
                 };
 
@@ -2646,7 +2649,7 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
                 .await;
 
                 let mut tx = match drk.lock_contract(deploy_auth).await {
-                    Ok(v) => v,
+                    Ok(t) => t,
                     Err(e) => {
                         eprintln!("Error creating contract lock tx: {e}");
                         exit(2);
@@ -2654,7 +2657,7 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
                 };
 
                 if let Err(e) = drk.attach_fee(&mut tx).await {
-                    eprintln!("Failed to attach the fee call to the transaction: {e:?}");
+                    eprintln!("Failed to attach the fee call to the transaction: {e}");
                     exit(2);
                 };