drk/interactive: previous command output as next command input

This commit is contained in:
skoupidi
2025-06-22 12:53:07 +03:00
parent 1af74b2e37
commit 5f889aae6f
12 changed files with 581 additions and 431 deletions

View File

@@ -17,7 +17,6 @@
*/
use std::{
io::{stdin, Cursor, Read},
process::exit,
str::FromStr,
};
@@ -39,28 +38,37 @@ use crate::{money::BALANCE_BASE10_DECIMALS, Drk};
pub async fn parse_tx_from_stdin() -> Result<Transaction> {
let mut buf = String::new();
stdin().read_to_string(&mut buf)?;
let Some(bytes) = base64::decode(buf.trim()) else {
eprintln!("Failed to decode transaction");
exit(2);
};
match base64::decode(buf.trim()) {
Some(bytes) => Ok(deserialize_async(&bytes).await?),
None => Err(Error::ParseFailed("Failed to decode transaction")),
}
}
Ok(deserialize_async(&bytes).await?)
/// Auxiliary function to parse a base64 encoded transaction from
/// provided input or fallback to stdin if its empty.
pub async fn parse_tx_from_input(input: &[String]) -> Result<Transaction> {
match input.len() {
0 => parse_tx_from_stdin().await,
1 => match base64::decode(input[0].trim()) {
Some(bytes) => Ok(deserialize_async(&bytes).await?),
None => Err(Error::ParseFailed("Failed to decode transaction")),
},
_ => Err(Error::ParseFailed("Multiline input provided")),
}
}
/// Auxiliary function to parse provided string into a values pair.
pub fn parse_value_pair(s: &str) -> Result<(u64, u64)> {
let v: Vec<&str> = s.split(':').collect();
if v.len() != 2 {
eprintln!("Invalid value pair. Use a pair such as 13.37:11.0");
exit(2);
return Err(Error::ParseFailed("Invalid value pair. Use a pair such as 13.37:11.0"))
}
let val0 = decode_base10(v[0], BALANCE_BASE10_DECIMALS, true);
let val1 = decode_base10(v[1], BALANCE_BASE10_DECIMALS, true);
if val0.is_err() || val1.is_err() {
eprintln!("Invalid value pair. Use a pair such as 13.37:11.0");
exit(2);
return Err(Error::ParseFailed("Invalid value pair. Use a pair such as 13.37:11.0"))
}
Ok((val0.unwrap(), val1.unwrap()))
@@ -70,22 +78,20 @@ pub fn parse_value_pair(s: &str) -> Result<(u64, u64)> {
pub async fn parse_token_pair(drk: &Drk, s: &str) -> Result<(TokenId, TokenId)> {
let v: Vec<&str> = s.split(':').collect();
if v.len() != 2 {
eprintln!("Invalid token pair. Use a pair such as:");
eprintln!("WCKD:MLDY");
eprintln!("or");
eprintln!("A7f1RKsCUUHrSXA7a9ogmwg8p3bs6F47ggsW826HD4yd:FCuoMii64H5Ee4eVWBjP18WTFS8iLUJmGi16Qti1xFQ2");
exit(2);
return Err(Error::ParseFailed(
"Invalid token pair. Use a pair such as:\nWCKD:MLDY\nor\n\
A7f1RKsCUUHrSXA7a9ogmwg8p3bs6F47ggsW826HD4yd:FCuoMii64H5Ee4eVWBjP18WTFS8iLUJmGi16Qti1xFQ2"
))
}
let tok0 = drk.get_token(v[0].to_string()).await;
let tok1 = drk.get_token(v[1].to_string()).await;
if tok0.is_err() || tok1.is_err() {
eprintln!("Invalid token pair. Use a pair such as:");
eprintln!("WCKD:MLDY");
eprintln!("or");
eprintln!("A7f1RKsCUUHrSXA7a9ogmwg8p3bs6F47ggsW826HD4yd:FCuoMii64H5Ee4eVWBjP18WTFS8iLUJmGi16Qti1xFQ2");
exit(2);
return Err(Error::ParseFailed(
"Invalid token pair. Use a pair such as:\nWCKD:MLDY\nor\n\
A7f1RKsCUUHrSXA7a9ogmwg8p3bs6F47ggsW826HD4yd:FCuoMii64H5Ee4eVWBjP18WTFS8iLUJmGi16Qti1xFQ2"
))
}
Ok((tok0.unwrap(), tok1.unwrap()))
@@ -107,7 +113,7 @@ pub async fn kaching() {
}
/// Auxiliary function to generate provided shell completions.
pub fn generate_completions(shell: &str) -> Result<()> {
pub fn generate_completions(shell: &str) -> Result<String> {
// Sub-commands
// Interactive
@@ -537,7 +543,15 @@ pub fn generate_completions(shell: &str) -> Result<()> {
Err(e) => return Err(Error::Custom(e)),
};
app.gen_completions_to("./drk", shell, &mut std::io::stdout());
let mut buf = vec![];
app.gen_completions_to("./drk", shell, &mut buf);
Ok(())
Ok(String::from_utf8(buf)?)
}
/// Auxiliary function to generate provided shell completions.
pub fn print_output(buf: &[String]) {
for line in buf {
println!("{line}");
}
}

View File

@@ -1631,24 +1631,26 @@ impl Drk {
}
/// Reset the DAO Merkle trees in the cache.
pub fn reset_dao_trees(&self) -> WalletDbResult<()> {
println!("Resetting DAO Merkle trees");
pub fn reset_dao_trees(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting DAO Merkle trees"));
if let Err(e) = self.cache.merkle_trees.remove(SLED_MERKLE_TREES_DAO_DAOS) {
println!("[reset_dao_trees] Resetting DAO DAOs Merkle tree failed: {e:?}");
output.push(format!("[reset_dao_trees] Resetting DAO DAOs Merkle tree failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
if let Err(e) = self.cache.merkle_trees.remove(SLED_MERKLE_TREES_DAO_PROPOSALS) {
println!("[reset_dao_trees] Resetting DAO Proposals Merkle tree failed: {e:?}");
output.push(format!(
"[reset_dao_trees] Resetting DAO Proposals Merkle tree failed: {e:?}"
));
return Err(WalletDbError::GenericError)
}
println!("Successfully reset DAO Merkle trees");
output.push(String::from("Successfully reset DAO Merkle trees"));
Ok(())
}
/// Reset confirmed DAOs in the wallet.
pub fn reset_daos(&self) -> WalletDbResult<()> {
println!("Resetting DAO confirmations");
pub fn reset_daos(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting DAO confirmations"));
let query = format!(
"UPDATE {} SET {} = NULL, {} = NULL, {} = NULL, {} = NULL;",
*DAO_DAOS_TABLE,
@@ -1658,15 +1660,19 @@ impl Drk {
DAO_DAOS_COL_CALL_INDEX,
);
self.wallet.exec_sql(&query, &[])?;
println!("Successfully unconfirmed DAOs");
output.push(String::from("Successfully unconfirmed DAOs"));
Ok(())
}
/// Reset confirmed DAOs in the wallet that were minted after
/// provided height.
pub fn unconfirm_daos_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Resetting DAO confirmations after: {height}");
pub fn unconfirm_daos_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Resetting DAO confirmations after: {height}"));
let query = format!(
"UPDATE {} SET {} = NULL, {} = NULL, {} = NULL, {} = NULL WHERE {} > ?1;",
*DAO_DAOS_TABLE,
@@ -1677,14 +1683,14 @@ impl Drk {
DAO_DAOS_COL_MINT_HEIGHT,
);
self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
println!("Successfully unconfirmed DAOs");
output.push(String::from("Successfully unconfirmed DAOs"));
Ok(())
}
/// Reset all DAO proposals in the wallet.
pub fn reset_dao_proposals(&self) -> WalletDbResult<()> {
println!("Resetting DAO proposals confirmations");
pub fn reset_dao_proposals(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting DAO proposals confirmations"));
let query = format!(
"UPDATE {} SET {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL;",
*DAO_PROPOSALS_TABLE,
@@ -1698,15 +1704,19 @@ impl Drk {
DAO_PROPOSALS_COL_EXEC_TX_HASH,
);
self.wallet.exec_sql(&query, &[])?;
println!("Successfully unconfirmed DAO proposals");
output.push(String::from("Successfully unconfirmed DAO proposals"));
Ok(())
}
/// Reset DAO proposals in the wallet that were minted after
/// provided height.
pub fn unconfirm_dao_proposals_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Resetting DAO proposals confirmations after: {height}");
pub fn unconfirm_dao_proposals_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Resetting DAO proposals confirmations after: {height}"));
let query = format!(
"UPDATE {} SET {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL WHERE {} > ?1;",
*DAO_PROPOSALS_TABLE,
@@ -1721,15 +1731,19 @@ impl Drk {
DAO_PROPOSALS_COL_MINT_HEIGHT,
);
self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
println!("Successfully unconfirmed DAO proposals");
output.push(String::from("Successfully unconfirmed DAO proposals"));
Ok(())
}
/// Reset execution information in the wallet for DAO proposals
/// that were executed after provided height.
pub fn unexec_dao_proposals_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Resetting DAO proposals execution information after: {height}");
pub fn unexec_dao_proposals_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Resetting DAO proposals execution information after: {height}"));
let query = format!(
"UPDATE {} SET {} = NULL, {} = NULL WHERE {} > ?1;",
*DAO_PROPOSALS_TABLE,
@@ -1738,29 +1752,33 @@ impl Drk {
DAO_PROPOSALS_COL_EXEC_HEIGHT,
);
self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
println!("Successfully reset DAO proposals execution information");
output.push(String::from("Successfully reset DAO proposals execution information"));
Ok(())
}
/// Reset all DAO votes in the wallet.
pub fn reset_dao_votes(&self) -> WalletDbResult<()> {
println!("Resetting DAO votes");
pub fn reset_dao_votes(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting DAO votes"));
let query = format!("DELETE FROM {};", *DAO_VOTES_TABLE);
self.wallet.exec_sql(&query, &[])?;
println!("Successfully reset DAO votes");
output.push(String::from("Successfully reset DAO votes"));
Ok(())
}
/// Remove the DAO votes in the wallet that were created after
/// provided height.
pub fn remove_dao_votes_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Removing DAO votes after: {height}");
pub fn remove_dao_votes_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Removing DAO votes after: {height}"));
let query =
format!("DELETE FROM {} WHERE {} > ?1;", *DAO_VOTES_TABLE, DAO_VOTES_COL_BLOCK_HEIGHT);
self.wallet.exec_sql(&query, rusqlite::params![height])?;
println!("Successfully removed DAO votes");
output.push(String::from("Successfully removed DAO votes"));
Ok(())
}

View File

@@ -85,22 +85,26 @@ impl Drk {
}
/// Reset all token deploy authorities frozen status in the wallet.
pub fn reset_deploy_authorities(&self) -> WalletDbResult<()> {
println!("Resetting deploy authorities frozen status");
pub fn reset_deploy_authorities(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting deploy authorities frozen status"));
let query = format!(
"UPDATE {} SET {} = 0, {} = NULL;",
*DEPLOY_AUTH_TABLE, DEPLOY_AUTH_COL_IS_FROZEN, DEPLOY_AUTH_COL_FREEZE_HEIGHT
);
self.wallet.exec_sql(&query, &[])?;
println!("Successfully reset deploy authorities frozen status");
output.push(String::from("Successfully reset deploy authorities frozen status"));
Ok(())
}
/// Remove deploy authorities frozen status in the wallet that
/// where frozen after provided height.
pub fn unfreeze_deploy_authorities_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Resetting deploy authorities frozen status after: {height}");
pub fn unfreeze_deploy_authorities_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Resetting deploy authorities frozen status after: {height}"));
let query = format!(
"UPDATE {} SET {} = 0, {} = NULL WHERE {} > ?1;",
*DEPLOY_AUTH_TABLE,
@@ -109,7 +113,7 @@ impl Drk {
DEPLOY_AUTH_COL_FREEZE_HEIGHT
);
self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
println!("Successfully reset deploy authorities frozen status");
output.push(String::from("Successfully reset deploy authorities frozen status"));
Ok(())
}

View File

@@ -47,7 +47,8 @@ use darkfi_serial::{deserialize_async, serialize_async};
use crate::{
cli_util::{
generate_completions, kaching, parse_token_pair, parse_tx_from_stdin, parse_value_pair,
generate_completions, kaching, parse_token_pair, parse_tx_from_input, parse_value_pair,
print_output,
},
money::BALANCE_BASE10_DECIMALS,
rpc::subscribe_blocks,
@@ -293,15 +294,8 @@ pub async fn interactive(drk: &DrkPtr, endpoint: &Url, history_path: &str, ex: &
let commands: Vec<&str> = line.split('|').collect();
// Process each command
let mut input = vec![];
let mut output = vec![];
for command in commands {
// Handle previous command output
// TODO: this is examplatory, we will actually use the input
// in our commands
for line in &input {
println!("{line}");
}
// Parse command parts
let parts: Vec<&str> = command.split_whitespace().collect();
if parts.is_empty() {
@@ -309,20 +303,21 @@ pub async fn interactive(drk: &DrkPtr, endpoint: &Url, history_path: &str, ex: &
}
// Handle command
let mut output = vec![];
let input = output;
output = vec![];
match parts[0] {
"help" => help(&mut output),
"kaching" => kaching().await,
"ping" => handle_ping(drk).await,
"completions" => handle_completions(&parts),
"wallet" => handle_wallet(drk, &parts).await,
"spend" => handle_spend(drk).await,
"unspend" => handle_unspend(drk, &parts).await,
"transfer" => handle_transfer(drk, &parts).await,
"otc" => handle_otc(drk, &parts).await,
"attach-fee" => handle_attach_fee(drk).await,
"inspect" => handle_inspect().await,
"broadcast" => handle_broadcast(drk).await,
"ping" => handle_ping(drk, &mut output).await,
"completions" => handle_completions(&parts, &mut output),
"wallet" => handle_wallet(drk, &parts, &input, &mut output).await,
"spend" => handle_spend(drk, &input, &mut output).await,
"unspend" => handle_unspend(drk, &parts, &mut output).await,
"transfer" => handle_transfer(drk, &parts, &mut output).await,
"otc" => handle_otc(drk, &parts, &input, &mut output).await,
"attach-fee" => handle_attach_fee(drk, &input, &mut output).await,
"inspect" => handle_inspect(&input, &mut output).await,
"broadcast" => handle_broadcast(drk, &input, &mut output).await,
"subscribe" => {
handle_subscribe(
drk,
@@ -331,24 +326,23 @@ pub async fn interactive(drk: &DrkPtr, endpoint: &Url, history_path: &str, ex: &
&subscription_task,
&shell_sender,
ex,
&mut output,
)
.await
}
"unsubscribe" => {
handle_unsubscribe(&mut subscription_active, &subscription_task).await
handle_unsubscribe(&mut subscription_active, &subscription_task, &mut output)
.await
}
"snooze" => snooze_active = true,
"unsnooze" => snooze_active = false,
"scan" => handle_scan(drk, &subscription_active, &parts).await,
_ => println!("Unreconized command: {}", parts[0]),
"scan" => handle_scan(drk, &subscription_active, &parts, &mut output).await,
_ => output.push(format!("Unreconized command: {}", parts[0])),
}
input = output
}
// Handle last command output
for line in input {
println!("{line}");
}
print_output(&output);
}
// Stop the subscription task if its active
@@ -461,88 +455,89 @@ async fn listen_for_line(
}
/// Auxiliary function to define the ping command handling.
async fn handle_ping(drk: &DrkPtr) {
if let Err(e) = drk.read().await.ping().await {
println!("Error while executing ping command: {e}")
async fn handle_ping(drk: &DrkPtr, output: &mut Vec<String>) {
if let Err(e) = drk.read().await.ping(output).await {
output.push(format!("Error while executing ping command: {e}"))
}
}
/// Auxiliary function to define the completions command handling.
fn handle_completions(parts: &[&str]) {
fn handle_completions(parts: &[&str], output: &mut Vec<String>) {
// Check correct command structure
if parts.len() != 2 {
println!("Malformed `completions` command");
println!("Usage: completions <shell>");
output.push(String::from("Malformed `completions` command"));
output.push(String::from("Usage: completions <shell>"));
return
}
if let Err(e) = generate_completions(parts[1]) {
println!("Error while executing completions command: {e}")
match generate_completions(parts[1]) {
Ok(completions) => output.push(completions),
Err(e) => output.push(format!("Error while executing completions command: {e}")),
}
}
/// Auxiliary function to define the wallet command handling.
async fn handle_wallet(drk: &DrkPtr, parts: &[&str]) {
async fn handle_wallet(drk: &DrkPtr, parts: &[&str], input: &[String], output: &mut Vec<String>) {
// Check correct command structure
if parts.len() < 2 {
println!("Malformed `wallet` command");
println!("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
output.push(String::from("Malformed `wallet` command"));
output.push(String::from("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)"));
return
}
// Handle subcommand
match parts[1] {
"initialize" => handle_wallet_initialize(drk).await,
"keygen" => handle_wallet_keygen(drk).await,
"balance" => handle_wallet_balance(drk).await,
"address" => handle_wallet_address(drk).await,
"addresses" => handle_wallet_addresses(drk).await,
"default-address" => handle_wallet_default_address(drk, parts).await,
"secrets" => handle_wallet_secrets(drk).await,
"import-secrets" => handle_wallet_import_secrets(drk).await,
"tree" => handle_wallet_tree(drk).await,
"coins" => handle_wallet_coins(drk).await,
"initialize" => handle_wallet_initialize(drk, output).await,
"keygen" => handle_wallet_keygen(drk, output).await,
"balance" => handle_wallet_balance(drk, output).await,
"address" => handle_wallet_address(drk, output).await,
"addresses" => handle_wallet_addresses(drk, output).await,
"default-address" => handle_wallet_default_address(drk, parts, output).await,
"secrets" => handle_wallet_secrets(drk, output).await,
"import-secrets" => handle_wallet_import_secrets(drk, input, output).await,
"tree" => handle_wallet_tree(drk, output).await,
"coins" => handle_wallet_coins(drk, output).await,
_ => {
println!("Unreconized wallet subcommand: {}", parts[1]);
println!("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
output.push(format!("Unreconized wallet subcommand: {}", parts[1]));
output.push(String::from("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)"));
}
}
}
/// Auxiliary function to define the wallet initialize subcommand handling.
async fn handle_wallet_initialize(drk: &DrkPtr) {
async fn handle_wallet_initialize(drk: &DrkPtr, output: &mut Vec<String>) {
let lock = drk.read().await;
if let Err(e) = lock.initialize_wallet().await {
println!("Error initializing wallet: {e:?}");
output.push(format!("Error initializing wallet: {e:?}"));
return
}
if let Err(e) = lock.initialize_money().await {
println!("Failed to initialize Money: {e:?}");
output.push(format!("Failed to initialize Money: {e:?}"));
return
}
if let Err(e) = lock.initialize_dao().await {
println!("Failed to initialize DAO: {e:?}");
output.push(format!("Failed to initialize DAO: {e:?}"));
return
}
if let Err(e) = lock.initialize_deployooor() {
println!("Failed to initialize Deployooor: {e:?}");
output.push(format!("Failed to initialize Deployooor: {e:?}"));
}
}
/// Auxiliary function to define the wallet keygen subcommand handling.
async fn handle_wallet_keygen(drk: &DrkPtr) {
if let Err(e) = drk.read().await.money_keygen().await {
println!("Failed to generate keypair: {e:?}");
async fn handle_wallet_keygen(drk: &DrkPtr, output: &mut Vec<String>) {
if let Err(e) = drk.read().await.money_keygen(output).await {
output.push(format!("Failed to generate keypair: {e:?}"));
}
}
/// Auxiliary function to define the wallet balance subcommand handling.
async fn handle_wallet_balance(drk: &DrkPtr) {
async fn handle_wallet_balance(drk: &DrkPtr, output: &mut Vec<String>) {
let lock = drk.read().await;
let balmap = match lock.money_balance().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch balances map: {e:?}");
output.push(format!("Failed to fetch balances map: {e:?}"));
return
}
};
@@ -550,7 +545,7 @@ async fn handle_wallet_balance(drk: &DrkPtr) {
let aliases_map = match lock.get_aliases_mapped_by_token().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch aliases map: {e:?}");
output.push(format!("Failed to fetch aliases map: {e:?}"));
return
}
};
@@ -569,26 +564,26 @@ async fn handle_wallet_balance(drk: &DrkPtr) {
}
if table.is_empty() {
println!("No unspent balances found");
output.push(String::from("No unspent balances found"));
} else {
println!("{table}");
output.push(format!("{table}"));
}
}
/// Auxiliary function to define the wallet address subcommand handling.
async fn handle_wallet_address(drk: &DrkPtr) {
async fn handle_wallet_address(drk: &DrkPtr, output: &mut Vec<String>) {
match drk.read().await.default_address().await {
Ok(address) => println!("{address}"),
Err(e) => println!("Failed to fetch default address: {e:?}"),
Ok(address) => output.push(format!("{address}")),
Err(e) => output.push(format!("Failed to fetch default address: {e:?}")),
}
}
/// Auxiliary function to define the wallet addresses subcommand handling.
async fn handle_wallet_addresses(drk: &DrkPtr) {
async fn handle_wallet_addresses(drk: &DrkPtr, output: &mut Vec<String>) {
let addresses = match drk.read().await.addresses().await {
Ok(a) => a,
Err(e) => {
println!("Failed to fetch addresses: {e:?}");
output.push(format!("Failed to fetch addresses: {e:?}"));
return
}
};
@@ -606,90 +601,102 @@ async fn handle_wallet_addresses(drk: &DrkPtr) {
}
if table.is_empty() {
println!("No addresses found");
output.push(String::from("No addresses found"));
} else {
println!("{table}");
output.push(format!("{table}"));
}
}
/// Auxiliary function to define the wallet default address subcommand handling.
async fn handle_wallet_default_address(drk: &DrkPtr, parts: &[&str]) {
async fn handle_wallet_default_address(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
if parts.len() != 3 {
println!("Malformed `wallet default-address` subcommand");
println!("Usage: wallet default-address <index>");
output.push(String::from("Malformed `wallet default-address` subcommand"));
output.push(String::from("Usage: wallet default-address <index>"));
return
}
let index = match usize::from_str(parts[2]) {
Ok(i) => i,
Err(e) => {
println!("Invalid address id: {e:?}");
output.push(format!("Invalid address id: {e:?}"));
return
}
};
if let Err(e) = drk.read().await.set_default_address(index) {
println!("Failed to set default address: {e:?}");
output.push(format!("Failed to set default address: {e:?}"));
}
}
/// Auxiliary function to define the wallet secrets subcommand handling.
async fn handle_wallet_secrets(drk: &DrkPtr) {
async fn handle_wallet_secrets(drk: &DrkPtr, output: &mut Vec<String>) {
match drk.read().await.get_money_secrets().await {
Ok(secrets) => {
for secret in secrets {
println!("{secret}");
output.push(format!("{secret}"));
}
}
Err(e) => println!("Failed to fetch secrets: {e:?}"),
Err(e) => output.push(format!("Failed to fetch secrets: {e:?}")),
}
}
/// Auxiliary function to define the wallet import secrets subcommand handling.
async fn handle_wallet_import_secrets(drk: &DrkPtr) {
async fn handle_wallet_import_secrets(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
let mut secrets = vec![];
// TODO: read from a file here not stdin
let lines = stdin().lines();
for (i, line) in lines.enumerate() {
if let Ok(line) = line {
// Parse input or read from stdin
if input.is_empty() {
for (i, line) in stdin().lines().enumerate() {
let Ok(line) = line else { continue };
let Ok(bytes) = bs58::decode(&line.trim()).into_vec() else {
println!("Warning: Failed to decode secret on line {i}");
output.push(format!("Warning: Failed to decode secret on line {i}"));
continue
};
let Ok(secret) = deserialize_async(&bytes).await else {
println!("Warning: Failed to deserialize secret on line {i}");
output.push(format!("Warning: Failed to deserialize secret on line {i}"));
continue
};
secrets.push(secret);
}
} else {
for (i, line) in input.iter().enumerate() {
let Ok(bytes) = bs58::decode(&line.trim()).into_vec() else {
output.push(format!("Warning: Failed to decode secret on line {i}"));
continue
};
let Ok(secret) = deserialize_async(&bytes).await else {
output.push(format!("Warning: Failed to deserialize secret on line {i}"));
continue
};
secrets.push(secret);
}
}
match drk.read().await.import_money_secrets(secrets).await {
match drk.read().await.import_money_secrets(secrets, output).await {
Ok(pubkeys) => {
for key in pubkeys {
println!("{key}");
output.push(format!("{key}"));
}
}
Err(e) => println!("Failed to import secrets: {e:?}"),
Err(e) => output.push(format!("Failed to import secrets: {e:?}")),
}
}
/// Auxiliary function to define the wallet tree subcommand handling.
async fn handle_wallet_tree(drk: &DrkPtr) {
// TODO: write to a file here not stdout
async fn handle_wallet_tree(drk: &DrkPtr, output: &mut Vec<String>) {
match drk.read().await.get_money_tree().await {
Ok(tree) => println!("{tree:#?}"),
Err(e) => println!("Failed to fetch tree: {e:?}"),
Ok(tree) => output.push(format!("{tree:#?}")),
Err(e) => output.push(format!("Failed to fetch tree: {e:?}")),
}
}
/// Auxiliary function to define the wallet coins subcommand handling.
async fn handle_wallet_coins(drk: &DrkPtr) {
async fn handle_wallet_coins(drk: &DrkPtr, output: &mut Vec<String>) {
let lock = drk.read().await;
let coins = match lock.get_coins(true).await {
Ok(c) => c,
Err(e) => {
println!("Failed to fetch coins: {e:?}");
output.push(format!("Failed to fetch coins: {e:?}"));
return
}
};
@@ -701,7 +708,7 @@ async fn handle_wallet_coins(drk: &DrkPtr) {
let aliases_map = match lock.get_aliases_mapped_by_token().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch aliases map: {e:?}");
output.push(format!("Failed to fetch aliases map: {e:?}"));
return
}
};
@@ -761,37 +768,37 @@ async fn handle_wallet_coins(drk: &DrkPtr) {
]);
}
println!("{table}");
output.push(format!("{table}"));
}
/// Auxiliary function to define the spend command handling.
async fn handle_spend(drk: &DrkPtr) {
let tx = match parse_tx_from_stdin().await {
async fn handle_spend(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
let tx = match parse_tx_from_input(input).await {
Ok(t) => t,
Err(e) => {
println!("Error while parsing transaction: {e}");
output.push(format!("Error while parsing transaction: {e}"));
return
}
};
if let Err(e) = drk.read().await.mark_tx_spend(&tx).await {
println!("Failed to mark transaction coins as spent: {e}")
if let Err(e) = drk.read().await.mark_tx_spend(&tx, output).await {
output.push(format!("Failed to mark transaction coins as spent: {e}"))
}
}
/// Auxiliary function to define the unspend command handling.
async fn handle_unspend(drk: &DrkPtr, parts: &[&str]) {
async fn handle_unspend(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
// Check correct command structure
if parts.len() != 2 {
println!("Malformed `unspend` command");
println!("Usage: unspend <coin>");
output.push(String::from("Malformed `unspend` command"));
output.push(String::from("Usage: unspend <coin>"));
return
}
let bytes = match bs58::decode(&parts[1]).into_vec() {
Ok(b) => b,
Err(e) => {
println!("Invalid coin: {e}");
output.push(format!("Invalid coin: {e}"));
return
}
};
@@ -799,7 +806,7 @@ async fn handle_unspend(drk: &DrkPtr, parts: &[&str]) {
let bytes: [u8; 32] = match bytes.try_into() {
Ok(b) => b,
Err(e) => {
println!("Invalid coin: {e:?}");
output.push(format!("Invalid coin: {e:?}"));
return
}
};
@@ -807,24 +814,24 @@ async fn handle_unspend(drk: &DrkPtr, parts: &[&str]) {
let elem: pallas::Base = match pallas::Base::from_repr(bytes).into() {
Some(v) => v,
None => {
println!("Invalid coin");
output.push(String::from("Invalid coin"));
return
}
};
if let Err(e) = drk.read().await.unspend_coin(&Coin::from(elem)).await {
println!("Failed to mark coin as unspent: {e}")
output.push(format!("Failed to mark coin as unspent: {e}"))
}
}
/// Auxiliary function to define the transfer command handling.
async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
async fn handle_transfer(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
// Check correct command structure
if parts.len() < 4 || parts.len() > 7 {
println!("Malformed `transfer` command");
println!(
"Usage: transfer [--half-split] <amount> <token> <recipient> [spend_hook] [user_data]"
);
output.push(String::from("Malformed `transfer` command"));
output.push(String::from(
"Usage: transfer [--half-split] <amount> <token> <recipient> [spend_hook] [user_data]",
));
return
}
@@ -838,7 +845,7 @@ async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
let amount = String::from(parts[index]);
if let Err(e) = f64::from_str(&amount) {
println!("Invalid amount: {e}");
output.push(format!("Invalid amount: {e}"));
return
}
index += 1;
@@ -847,7 +854,7 @@ async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
let token_id = match lock.get_token(String::from(parts[index])).await {
Ok(t) => t,
Err(e) => {
println!("Invalid token alias: {e}");
output.push(format!("Invalid token alias: {e}"));
return
}
};
@@ -856,7 +863,7 @@ async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
let rcpt = match PublicKey::from_str(parts[index]) {
Ok(r) => r,
Err(e) => {
println!("Invalid recipient: {e}");
output.push(format!("Invalid recipient: {e}"));
return
}
};
@@ -866,7 +873,7 @@ async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
match FuncId::from_str(parts[index]) {
Ok(s) => Some(s),
Err(e) => {
println!("Invalid spend hook: {e}");
output.push(format!("Invalid spend hook: {e}"));
return
}
}
@@ -879,7 +886,7 @@ async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
let bytes = match bs58::decode(&parts[index]).into_vec() {
Ok(b) => b,
Err(e) => {
println!("Invalid user data: {e}");
output.push(format!("Invalid user data: {e}"));
return
}
};
@@ -887,7 +894,7 @@ async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
let bytes: [u8; 32] = match bytes.try_into() {
Ok(b) => b,
Err(e) => {
println!("Invalid user data: {e:?}");
output.push(format!("Invalid user data: {e:?}"));
return
}
};
@@ -895,7 +902,7 @@ async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
let elem: pallas::Base = match pallas::Base::from_repr(bytes).into() {
Some(v) => v,
None => {
println!("Invalid user data");
output.push(String::from("Invalid user data"));
return
}
};
@@ -905,48 +912,47 @@ async fn handle_transfer(drk: &DrkPtr, parts: &[&str]) {
None
};
// TODO: write to a file here not stdout
match lock.transfer(&amount, token_id, rcpt, spend_hook, user_data, half_split).await {
Ok(t) => println!("{}", base64::encode(&serialize_async(&t).await)),
Err(e) => println!("Failed to create payment transaction: {e}"),
Ok(t) => output.push(base64::encode(&serialize_async(&t).await)),
Err(e) => output.push(format!("Failed to create payment transaction: {e}")),
}
}
/// Auxiliary function to define the otc command handling.
async fn handle_otc(drk: &DrkPtr, parts: &[&str]) {
async fn handle_otc(drk: &DrkPtr, parts: &[&str], input: &[String], output: &mut Vec<String>) {
// Check correct command structure
if parts.len() < 2 {
println!("Malformed `otc` command");
println!("Usage: otc (init|join|inspect|sign)");
output.push(String::from("Malformed `otc` command"));
output.push(String::from("Usage: otc (init|join|inspect|sign)"));
return
}
// Handle subcommand
match parts[1] {
"init" => handle_otc_init(drk, parts).await,
"join" => handle_otc_join(drk, parts).await,
"inspect" => handle_otc_inspect(drk, parts).await,
"sign" => handle_otc_sign(drk, parts).await,
"init" => handle_otc_init(drk, parts, output).await,
"join" => handle_otc_join(drk, parts, input, output).await,
"inspect" => handle_otc_inspect(drk, parts, input, output).await,
"sign" => handle_otc_sign(drk, parts, input, output).await,
_ => {
println!("Unreconized OTC subcommand: {}", parts[1]);
println!("Usage: otc (init|join|inspect|sign)");
output.push(format!("Unreconized OTC subcommand: {}", parts[1]));
output.push(String::from("Usage: otc (init|join|inspect|sign)"));
}
}
}
/// Auxiliary function to define the otc init subcommand handling.
async fn handle_otc_init(drk: &DrkPtr, parts: &[&str]) {
async fn handle_otc_init(drk: &DrkPtr, parts: &[&str], output: &mut Vec<String>) {
// Check correct subcommand structure
if parts.len() != 4 {
println!("Malformed `otc init` subcommand");
println!("Usage: otc init <value_pair> <token_pair>");
output.push(String::from("Malformed `otc init` subcommand"));
output.push(String::from("Usage: otc init <value_pair> <token_pair>"));
return
}
let value_pair = match parse_value_pair(parts[2]) {
Ok(v) => v,
Err(e) => {
println!("Invalid value pair: {e}");
output.push(format!("Invalid value pair: {e}"));
return
}
};
@@ -955,153 +961,174 @@ async fn handle_otc_init(drk: &DrkPtr, parts: &[&str]) {
let token_pair = match parse_token_pair(&lock, parts[3]).await {
Ok(t) => t,
Err(e) => {
println!("Invalid token pair: {e}");
output.push(format!("Invalid token pair: {e}"));
return
}
};
match lock.init_swap(value_pair, token_pair, None, None, None).await {
Ok(half) => println!("{}", base64::encode(&serialize_async(&half).await)),
Err(e) => eprintln!("Failed to create swap transaction half: {e}"),
Ok(half) => output.push(base64::encode(&serialize_async(&half).await)),
Err(e) => output.push(format!("Failed to create swap transaction half: {e}")),
}
}
/// Auxiliary function to define the otc join subcommand handling.
async fn handle_otc_join(drk: &DrkPtr, parts: &[&str]) {
async fn handle_otc_join(drk: &DrkPtr, parts: &[&str], input: &[String], output: &mut Vec<String>) {
// Check correct subcommand structure
if parts.len() != 2 {
println!("Malformed `otc join` subcommand");
println!("Usage: otc join");
output.push(String::from("Malformed `otc join` subcommand"));
output.push(String::from("Usage: otc join"));
return
}
// TODO: read from a file here not stdin
let mut buf = String::new();
if let Err(e) = stdin().read_to_string(&mut buf) {
println!("Failed to read from stdin: {e}");
return
// Parse line from input or fallback to stdin if its empty
let buf = match input.len() {
0 => {
let mut buf = String::new();
if let Err(e) = stdin().read_to_string(&mut buf) {
output.push(format!("Failed to read from stdin: {e}"));
return
};
buf
}
1 => input[0].clone(),
_ => {
output.push(String::from("Multiline input provided"));
return
}
};
let Some(bytes) = base64::decode(buf.trim()) else {
println!("Failed to decode partial swap data");
output.push(String::from("Failed to decode partial swap data"));
return
};
let partial: PartialSwapData = match deserialize_async(&bytes).await {
Ok(p) => p,
Err(e) => {
println!("Failed to deserialize partial swap data: {e}");
output.push(format!("Failed to deserialize partial swap data: {e}"));
return
}
};
match drk.read().await.join_swap(partial, None, None, None).await {
Ok(tx) => println!("{}", base64::encode(&serialize_async(&tx).await)),
Err(e) => eprintln!("Failed to create a join swap transaction: {e}"),
Ok(tx) => output.push(base64::encode(&serialize_async(&tx).await)),
Err(e) => output.push(format!("Failed to create a join swap transaction: {e}")),
}
}
/// Auxiliary function to define the otc inspect subcommand handling.
async fn handle_otc_inspect(drk: &DrkPtr, parts: &[&str]) {
async fn handle_otc_inspect(
drk: &DrkPtr,
parts: &[&str],
input: &[String],
output: &mut Vec<String>,
) {
// Check correct subcommand structure
if parts.len() != 2 {
println!("Malformed `otc inspect` subcommand");
println!("Usage: otc inspect");
output.push(String::from("Malformed `otc inspect` subcommand"));
output.push(String::from("Usage: otc inspect"));
return
}
// TODO: read from a file here not stdin
let mut buf = String::new();
if let Err(e) = stdin().read_to_string(&mut buf) {
println!("Failed to read from stdin: {e}");
return
// Parse line from input or fallback to stdin if its empty
let buf = match input.len() {
0 => {
let mut buf = String::new();
if let Err(e) = stdin().read_to_string(&mut buf) {
output.push(format!("Failed to read from stdin: {e}"));
return
};
buf
}
1 => input[0].clone(),
_ => {
output.push(String::from("Multiline input provided"));
return
}
};
let Some(bytes) = base64::decode(buf.trim()) else {
println!("Failed to decode swap transaction");
output.push(String::from("Failed to decode swap transaction"));
return
};
if let Err(e) = drk.read().await.inspect_swap(bytes).await {
println!("Failed to inspect swap: {e}");
if let Err(e) = drk.read().await.inspect_swap(bytes, output).await {
output.push(format!("Failed to inspect swap: {e}"));
}
}
/// Auxiliary function to define the otc sign subcommand handling.
async fn handle_otc_sign(drk: &DrkPtr, parts: &[&str]) {
async fn handle_otc_sign(drk: &DrkPtr, parts: &[&str], input: &[String], output: &mut Vec<String>) {
// Check correct subcommand structure
if parts.len() != 2 {
println!("Malformed `otc sign` subcommand");
println!("Usage: otc sign");
output.push(String::from("Malformed `otc sign` subcommand"));
output.push(String::from("Usage: otc sign"));
return
}
// TODO: read from a file here not stdin
let mut tx = match parse_tx_from_stdin().await {
let mut tx = match parse_tx_from_input(input).await {
Ok(t) => t,
Err(e) => {
println!("Error while parsing transaction: {e}");
output.push(format!("Error while parsing transaction: {e}"));
return
}
};
match drk.read().await.sign_swap(&mut tx).await {
Ok(_) => println!("{}", base64::encode(&serialize_async(&tx).await)),
Err(e) => println!("Failed to sign joined swap transaction: {e}"),
Ok(_) => output.push(base64::encode(&serialize_async(&tx).await)),
Err(e) => output.push(format!("Failed to sign joined swap transaction: {e}")),
}
}
/// Auxiliary function to define the attach fee command handling.
async fn handle_attach_fee(drk: &DrkPtr) {
// TODO: read from a file here not stdin
let mut tx = match parse_tx_from_stdin().await {
async fn handle_attach_fee(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
let mut tx = match parse_tx_from_input(input).await {
Ok(t) => t,
Err(e) => {
println!("Error while parsing transaction: {e}");
output.push(format!("Error while parsing transaction: {e}"));
return
}
};
match drk.read().await.attach_fee(&mut tx).await {
Ok(_) => println!("{}", base64::encode(&serialize_async(&tx).await)),
Err(e) => println!("Failed to attach the fee call to the transaction: {e}"),
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 inspect command handling.
async fn handle_inspect() {
// TODO: read from a file here not stdin
match parse_tx_from_stdin().await {
Ok(tx) => println!("{tx:#?}"),
Err(e) => println!("Error while parsing transaction: {e}"),
async fn handle_inspect(input: &[String], output: &mut Vec<String>) {
match parse_tx_from_input(input).await {
Ok(tx) => output.push(format!("{tx:#?}")),
Err(e) => output.push(format!("Error while parsing transaction: {e}")),
}
}
/// Auxiliary function to define the broadcast command handling.
async fn handle_broadcast(drk: &DrkPtr) {
// TODO: read from a file here not stdin
let tx = match parse_tx_from_stdin().await {
async fn handle_broadcast(drk: &DrkPtr, input: &[String], output: &mut Vec<String>) {
let tx = match parse_tx_from_input(input).await {
Ok(t) => t,
Err(e) => {
println!("Error while parsing transaction: {e}");
output.push(format!("Error while parsing transaction: {e}"));
return
}
};
let lock = drk.read().await;
if let Err(e) = lock.simulate_tx(&tx).await {
println!("Failed to simulate tx: {e}");
output.push(format!("Failed to simulate tx: {e}"));
return
};
if let Err(e) = lock.mark_tx_spend(&tx).await {
println!("Failed to mark transaction coins as spent: {e}");
if let Err(e) = lock.mark_tx_spend(&tx, output).await {
output.push(format!("Failed to mark transaction coins as spent: {e}"));
return
};
match lock.broadcast_tx(&tx).await {
Ok(txid) => println!("Transaction ID: {txid}"),
Err(e) => println!("Failed to broadcast transaction: {e}"),
match lock.broadcast_tx(&tx, output).await {
Ok(txid) => output.push(format!("Transaction ID: {txid}")),
Err(e) => output.push(format!("Failed to broadcast transaction: {e}")),
}
}
@@ -1113,18 +1140,13 @@ async fn handle_subscribe(
subscription_task: &StoppableTaskPtr,
shell_sender: &Sender<Vec<String>>,
ex: &ExecutorPtr,
output: &mut Vec<String>,
) {
if *subscription_active {
println!("Subscription is already active!");
output.push(String::from("Subscription is already active!"));
return
}
if let Err(e) = drk.read().await.scan_blocks().await {
println!("Failed during scanning: {e:?}");
return
}
println!("Finished scanning blockchain");
// Start the subcristion task
let drk_ = drk.clone();
let endpoint_ = endpoint.clone();
@@ -1146,9 +1168,13 @@ async fn handle_subscribe(
}
/// Auxiliary function to define the unsubscribe command handling.
async fn handle_unsubscribe(subscription_active: &mut bool, subscription_task: &StoppableTaskPtr) {
async fn handle_unsubscribe(
subscription_active: &mut bool,
subscription_task: &StoppableTaskPtr,
output: &mut Vec<String>,
) {
if !*subscription_active {
println!("Subscription is already inactive!");
output.push(String::from("Subscription is already inactive!"));
return
}
subscription_task.stop().await;
@@ -1156,15 +1182,20 @@ async fn handle_unsubscribe(subscription_active: &mut bool, subscription_task: &
}
/// Auxiliary function to define the scan command handling.
async fn handle_scan(drk: &DrkPtr, subscription_active: &bool, parts: &[&str]) {
async fn handle_scan(
drk: &DrkPtr,
subscription_active: &bool,
parts: &[&str],
output: &mut Vec<String>,
) {
if *subscription_active {
println!("Subscription is already active!");
output.push(String::from("Subscription is already active!"));
return
}
// Check correct command structure
if parts.len() != 1 && parts.len() != 3 {
println!("Malformed `scan` command");
output.push(String::from("Malformed `scan` command"));
return
}
@@ -1172,28 +1203,28 @@ async fn handle_scan(drk: &DrkPtr, subscription_active: &bool, parts: &[&str]) {
let lock = drk.read().await;
if parts.len() == 3 {
if parts[1] != "--reset" {
println!("Malformed `scan` command");
println!("Usage: scan --reset <height>");
output.push(String::from("Malformed `scan` command"));
output.push(String::from("Usage: scan --reset <height>"));
return
}
let height = match u32::from_str(parts[2]) {
Ok(h) => h,
Err(e) => {
println!("Invalid reset height: {e:?}");
output.push(format!("Invalid reset height: {e:?}"));
return
}
};
if let Err(e) = lock.reset_to_height(height) {
println!("Failed during wallet reset: {e:?}");
if let Err(e) = lock.reset_to_height(height, output) {
output.push(format!("Failed during wallet reset: {e:?}"));
return
}
}
if let Err(e) = lock.scan_blocks().await {
println!("Failed during scanning: {e:?}");
if let Err(e) = lock.scan_blocks(output).await {
output.push(format!("Failed during scanning: {e:?}"));
return
}
println!("Finished scanning blockchain");
output.push(String::from("Finished scanning blockchain"));
}

View File

@@ -133,20 +133,20 @@ impl Drk {
}
/// Auxiliary function to completely reset wallet state.
pub fn reset(&self) -> WalletDbResult<()> {
println!("Resetting full wallet state");
self.reset_scanned_blocks()?;
self.reset_money_tree()?;
self.reset_money_smt()?;
self.reset_money_coins()?;
self.reset_mint_authorities()?;
self.reset_dao_trees()?;
self.reset_daos()?;
self.reset_dao_proposals()?;
self.reset_dao_votes()?;
self.reset_deploy_authorities()?;
self.reset_tx_history()?;
println!("Successfully reset full wallet state");
pub fn reset(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting full wallet state"));
self.reset_scanned_blocks(output)?;
self.reset_money_tree(output)?;
self.reset_money_smt(output)?;
self.reset_money_coins(output)?;
self.reset_mint_authorities(output)?;
self.reset_dao_trees(output)?;
self.reset_daos(output)?;
self.reset_dao_proposals(output)?;
self.reset_dao_votes(output)?;
self.reset_deploy_authorities(output)?;
self.reset_tx_history(output)?;
output.push(String::from("Successfully reset full wallet state"));
Ok(())
}
}

View File

@@ -55,6 +55,7 @@ use darkfi_serial::{deserialize_async, serialize_async};
use drk::{
cli_util::{
generate_completions, kaching, parse_token_pair, parse_tx_from_stdin, parse_value_pair,
print_output,
},
dao::{DaoParams, ProposalRecord},
interactive::interactive,
@@ -687,11 +688,16 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
args.fun,
)
.await;
drk.ping().await?;
let mut output = vec![];
drk.ping(&mut output).await?;
print_output(&output);
drk.stop_rpc_client().await
}
Subcmd::Completions { shell } => generate_completions(&shell),
Subcmd::Completions { shell } => {
println!("{}", generate_completions(&shell)?);
Ok(())
}
Subcmd::Wallet { command } => {
let drk = new_wallet(
@@ -725,10 +731,12 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
}
WalletSubcmd::Keygen => {
if let Err(e) = drk.money_keygen().await {
let mut output = vec![];
if let Err(e) = drk.money_keygen(&mut output).await {
eprintln!("Failed to generate keypair: {e:?}");
exit(2);
}
print_output(&output);
}
WalletSubcmd::Balance => {
@@ -817,8 +825,12 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
}
}
let pubkeys = match drk.import_money_secrets(secrets).await {
Ok(p) => p,
let mut output = vec![];
let pubkeys = match drk.import_money_secrets(secrets, &mut output).await {
Ok(p) => {
print_output(&output);
p
}
Err(e) => {
eprintln!("Failed to import secret keys into wallet: {e:?}");
exit(2);
@@ -922,10 +934,12 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
)
.await;
if let Err(e) = drk.mark_tx_spend(&tx).await {
let mut output = vec![];
if let Err(e) = drk.mark_tx_spend(&tx, &mut output).await {
eprintln!("Failed to mark transaction coins as spent: {e:?}");
exit(2);
};
print_output(&output);
Ok(())
}
@@ -1119,10 +1133,12 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
args.fun,
)
.await;
if let Err(e) = drk.inspect_swap(bytes).await {
let mut output = vec![];
if let Err(e) = drk.inspect_swap(bytes, &mut output).await {
eprintln!("Failed to inspect swap: {e:?}");
exit(2);
};
print_output(&output);
Ok(())
}
@@ -1953,18 +1969,20 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
exit(2);
};
if let Err(e) = drk.mark_tx_spend(&tx).await {
let mut output = vec![];
if let Err(e) = drk.mark_tx_spend(&tx, &mut output).await {
eprintln!("Failed to mark transaction coins as spent: {e:?}");
exit(2);
};
let txid = match drk.broadcast_tx(&tx).await {
let txid = match drk.broadcast_tx(&tx, &mut output).await {
Ok(t) => t,
Err(e) => {
eprintln!("Failed to broadcast transaction: {e:?}");
exit(2);
}
};
print_output(&output);
println!("Transaction ID: {txid}");
@@ -1982,17 +2000,19 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
)
.await;
let mut output = vec![];
if let Some(height) = reset {
if let Err(e) = drk.reset_to_height(height) {
if let Err(e) = drk.reset_to_height(height, &mut output) {
eprintln!("Failed during wallet reset: {e:?}");
exit(2);
}
}
if let Err(e) = drk.scan_blocks().await {
if let Err(e) = drk.scan_blocks(&mut output).await {
eprintln!("Failed during scanning: {e:?}");
exit(2);
}
print_output(&output);
println!("Finished scanning blockchain");
drk.stop_rpc_client().await

View File

@@ -132,8 +132,8 @@ impl Drk {
}
/// Generate a new keypair and place it into the wallet.
pub async fn money_keygen(&self) -> WalletDbResult<()> {
println!("Generating a new keypair");
pub async fn money_keygen(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Generating a new keypair"));
// TODO: We might want to have hierarchical deterministic key derivation.
let keypair = Keypair::random(&mut OsRng);
@@ -155,8 +155,8 @@ impl Drk {
],
)?;
println!("New address:");
println!("{}", keypair.public);
output.push(String::from("New address:"));
output.push(format!("{}", keypair.public));
Ok(())
}
@@ -297,7 +297,11 @@ impl Drk {
/// Import given secret keys into the wallet.
/// If the key already exists, it will be skipped.
/// Returns the respective PublicKey objects for the imported keys.
pub async fn import_money_secrets(&self, secrets: Vec<SecretKey>) -> Result<Vec<PublicKey>> {
pub async fn import_money_secrets(
&self,
secrets: Vec<SecretKey>,
output: &mut Vec<String>,
) -> Result<Vec<PublicKey>> {
let existing_secrets = self.get_money_secrets().await?;
let mut ret = Vec::with_capacity(secrets.len());
@@ -305,7 +309,7 @@ impl Drk {
for secret in secrets {
// Check if secret already exists
if existing_secrets.contains(&secret) {
println!("Existing key found: {secret}");
output.push(format!("Existing key found: {secret}"));
continue
}
@@ -1014,7 +1018,7 @@ impl Drk {
}
/// Mark provided transaction input coins as spent.
pub async fn mark_tx_spend(&self, tx: &Transaction) -> Result<()> {
pub async fn mark_tx_spend(&self, tx: &Transaction, output: &mut Vec<String>) -> Result<()> {
// Create a cache of all our own nullifiers
let mut owncoins_nullifiers = BTreeMap::new();
for coin in self.get_coins(true).await? {
@@ -1025,13 +1029,13 @@ impl Drk {
}
let tx_hash = tx.hash().to_string();
println!("[mark_tx_spend] Processing transaction: {tx_hash}");
output.push(format!("[mark_tx_spend] Processing transaction: {tx_hash}"));
for (i, call) in tx.calls.iter().enumerate() {
if call.data.contract_id != *MONEY_CONTRACT_ID {
continue
}
println!("[mark_tx_spend] Found Money contract in call {i}");
output.push(format!("[mark_tx_spend] Found Money contract in call {i}"));
let nullifiers = self.money_call_nullifiers(call).await?;
self.mark_spent_coins(None, &owncoins_nullifiers, &nullifiers, &None, &tx_hash)?;
}
@@ -1101,57 +1105,67 @@ impl Drk {
}
/// Reset the Money Merkle tree in the cache.
pub fn reset_money_tree(&self) -> WalletDbResult<()> {
println!("Resetting Money Merkle tree");
pub fn reset_money_tree(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting Money Merkle tree"));
if let Err(e) = self.cache.merkle_trees.remove(SLED_MERKLE_TREES_MONEY) {
println!("[reset_money_tree] Resetting Money Merkle tree failed: {e:?}");
output.push(format!("[reset_money_tree] Resetting Money Merkle tree failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
println!("Successfully reset Money Merkle tree");
output.push(String::from("Successfully reset Money Merkle tree"));
Ok(())
}
/// Reset the Money nullifiers Sparse Merkle Tree in the cache.
pub fn reset_money_smt(&self) -> WalletDbResult<()> {
println!("Resetting Money Sparse Merkle tree");
pub fn reset_money_smt(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting Money Sparse Merkle tree"));
if let Err(e) = self.cache.money_smt.clear() {
println!("[reset_money_smt] Resetting Money Sparse Merkle tree failed: {e:?}");
output.push(format!(
"[reset_money_smt] Resetting Money Sparse Merkle tree failed: {e:?}"
));
return Err(WalletDbError::GenericError)
}
println!("Successfully reset Money Sparse Merkle tree");
output.push(String::from("Successfully reset Money Sparse Merkle tree"));
Ok(())
}
/// Reset the Money coins in the wallet.
pub fn reset_money_coins(&self) -> WalletDbResult<()> {
println!("Resetting coins");
pub fn reset_money_coins(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting coins"));
let query = format!("DELETE FROM {};", *MONEY_COINS_TABLE);
self.wallet.exec_sql(&query, &[])?;
println!("Successfully reset coins");
output.push(String::from("Successfully reset coins"));
Ok(())
}
/// Remove the Money coins in the wallet that were created after
/// provided height.
pub fn remove_money_coins_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Removing coins after: {height}");
pub fn remove_money_coins_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Removing coins after: {height}"));
let query = format!(
"DELETE FROM {} WHERE {} > ?1;",
*MONEY_COINS_TABLE, MONEY_COINS_COL_CREATION_HEIGHT
);
self.wallet.exec_sql(&query, rusqlite::params![height])?;
println!("Successfully removed coins");
output.push(String::from("Successfully removed coins"));
Ok(())
}
/// Mark the Money coins in the wallet that were spent after
/// provided height as unspent.
pub fn unspent_money_coins_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Unspenting coins after: {height}");
pub fn unspent_money_coins_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Unspenting coins after: {height}"));
let query = format!(
"UPDATE {} SET {} = 0, {} = NULL, {} = '=' WHERE {} > ?1;",
*MONEY_COINS_TABLE,
@@ -1161,7 +1175,7 @@ impl Drk {
MONEY_COINS_COL_SPENT_HEIGHT
);
self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
println!("Successfully unspent coins");
output.push(String::from("Successfully unspent coins"));
Ok(())
}

View File

@@ -284,7 +284,7 @@ impl Drk {
/// Scans the blockchain for wallet relevant transactions,
/// starting from the last scanned block. If a reorg has happened,
/// we revert to its previous height and then scan from there.
pub async fn scan_blocks(&self) -> WalletDbResult<()> {
pub async fn scan_blocks(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
// Grab last scanned block height
let (mut height, hash) = self.get_last_scanned_block()?;
@@ -294,7 +294,7 @@ impl Drk {
// Check if block was found
Err(Error::JsonRpcError((-32121, _))) => None,
Err(e) => {
eprintln!("[scan_blocks] RPC client request failed: {e:?}");
output.push(format!("[scan_blocks] RPC client request failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
};
@@ -302,7 +302,7 @@ impl Drk {
// Check if a reorg has happened
if block.is_none() || hash != block.unwrap().hash().to_string() {
// Find the exact block height the reorg happened
println!("A reorg has happened, finding last known common block...");
output.push(String::from("A reorg has happened, finding last known common block..."));
height = height.saturating_sub(1);
while height != 0 {
// Grab our scanned block hash for that height
@@ -314,7 +314,7 @@ impl Drk {
// Check if block was found
Err(Error::JsonRpcError((-32121, _))) => None,
Err(e) => {
eprintln!("[scan_blocks] RPC client request failed: {e:?}");
output.push(format!("[scan_blocks] RPC client request failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
};
@@ -326,8 +326,8 @@ impl Drk {
}
// Reset to its height
println!("Last common block found: {height} - {scanned_block_hash}");
self.reset_to_height(height)?;
output.push(format!("Last common block found: {height} - {scanned_block_hash}"));
self.reset_to_height(height, output)?;
break
}
}
@@ -335,7 +335,7 @@ impl Drk {
// If last scanned block is genesis(0) we reset,
// otherwise continue with the next block height.
if height == 0 {
self.reset()?;
self.reset(output)?;
} else {
height += 1;
}
@@ -344,22 +344,24 @@ impl Drk {
let mut scan_cache = match self.scan_cache().await {
Ok(c) => c,
Err(e) => {
eprintln!("[scan_blocks] Generating scan cache failed: {e:?}");
output.push(format!("[scan_blocks] Generating scan cache failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
};
loop {
// Grab last confirmed block
println!("Requested to scan from block number: {height}");
output.push(format!("Requested to scan from block number: {height}"));
let (last_height, last_hash) = match self.get_last_confirmed_block().await {
Ok(last) => last,
Err(e) => {
eprintln!("[scan_blocks] RPC client request failed: {e:?}");
output.push(format!("[scan_blocks] RPC client request failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
};
println!("Last confirmed block reported by darkfid: {last_height} - {last_hash}");
output.push(format!(
"Last confirmed block reported by darkfid: {last_height} - {last_hash}"
));
// Already scanned last confirmed block
if height > last_height {
@@ -367,21 +369,21 @@ impl Drk {
}
while height <= last_height {
println!("Requesting block {height}...");
output.push(format!("Requesting block {height}..."));
let block = match self.get_block_by_height(height).await {
Ok(b) => b,
Err(e) => {
eprintln!("[scan_blocks] RPC client request failed: {e:?}");
output.push(format!("[scan_blocks] RPC client request failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
};
println!("Block {height} received! Scanning block...");
output.push(format!("Block {height} received! Scanning block..."));
if let Err(e) = self.scan_block(&mut scan_cache, &block).await {
eprintln!("[scan_blocks] Scan block failed: {e:?}");
output.push(format!("[scan_blocks] Scan block failed: {e:?}"));
return Err(WalletDbError::GenericError)
};
for msg in scan_cache.flush_messages() {
println!("{msg}");
output.push(msg);
}
height += 1;
}
@@ -416,8 +418,8 @@ impl Drk {
/// Broadcast a given transaction to darkfid and forward onto the network.
/// Returns the transaction ID upon success.
pub async fn broadcast_tx(&self, tx: &Transaction) -> Result<String> {
println!("Broadcasting transaction...");
pub async fn broadcast_tx(&self, tx: &Transaction, output: &mut Vec<String>) -> Result<String> {
output.push(String::from("Broadcasting transaction..."));
let params =
JsonValue::Array(vec![JsonValue::String(base64::encode(&serialize_async(tx).await))]);
@@ -524,13 +526,13 @@ impl Drk {
}
/// Auxiliary function to ping configured darkfid daemon for liveness.
pub async fn ping(&self) -> Result<()> {
println!("Executing ping request to darkfid...");
pub async fn ping(&self, output: &mut Vec<String>) -> Result<()> {
output.push(String::from("Executing ping request to darkfid..."));
let latency = Instant::now();
let rep = self.darkfid_daemon_request("ping", &JsonValue::Array(vec![])).await?;
let latency = latency.elapsed();
println!("Got reply: {rep:?}");
println!("Latency: {latency:?}");
output.push(format!("Got reply: {rep:?}"));
output.push(format!("Latency: {latency:?}"));
Ok(())
}
@@ -569,17 +571,31 @@ pub async fn subscribe_blocks(
endpoint: Url,
ex: &ExecutorPtr,
) -> Result<()> {
// Grab last confirmed block height
// First we do a clean scan
let lock = drk.read().await;
let mut output = vec![];
if let Err(e) = lock.scan_blocks(&mut output).await {
let err_msg = format!("Failed during scanning: {e:?}");
output.push(err_msg.clone());
shell_sender.send(output).await?;
return Err(Error::Custom(err_msg))
}
output.push(String::from("Finished scanning blockchain"));
shell_sender.send(output).await?;
// Grab last confirmed block height
let (last_confirmed_height, _) = lock.get_last_confirmed_block().await?;
// Handle genesis(0) block
if last_confirmed_height == 0 {
if let Err(e) = lock.scan_blocks().await {
return Err(Error::DatabaseError(format!(
"[subscribe_blocks] Scanning from genesis block failed: {e:?}"
)))
output = vec![];
if let Err(e) = lock.scan_blocks(&mut output).await {
let err_msg = format!("[subscribe_blocks] Scanning from genesis block failed: {e:?}");
output.push(err_msg.clone());
shell_sender.send(output).await?;
return Err(Error::Custom(err_msg))
}
shell_sender.send(output).await?;
}
// Grab last confirmed block again
@@ -589,23 +605,23 @@ pub async fn subscribe_blocks(
let (mut last_scanned_height, last_scanned_hash) = match lock.get_last_scanned_block() {
Ok(last) => last,
Err(e) => {
return Err(Error::DatabaseError(format!(
"[subscribe_blocks] Retrieving last scanned block failed: {e:?}"
)))
let err_msg = format!("[subscribe_blocks] Retrieving last scanned block failed: {e:?}");
shell_sender.send(vec![err_msg.clone()]).await?;
return Err(Error::Custom(err_msg))
}
};
// Check if other blocks have been created
if last_confirmed_height != last_scanned_height || last_confirmed_hash != last_scanned_hash {
let err_msg = String::from("[subscribe_blocks] Blockchain not fully scanned");
shell_sender
.send(vec![
String::from("Warning: Last scanned block is not the last confirmed block."),
String::from("You should first fully scan the blockchain, and then subscribe"),
err_msg.clone(),
])
.await?;
return Err(Error::DatabaseError(
"[subscribe_blocks] Blockchain not fully scanned".to_string(),
))
return Err(Error::Custom(err_msg))
}
let mut shell_message =
@@ -644,12 +660,13 @@ pub async fn subscribe_blocks(
shell_sender.send(shell_message).await?;
drop(lock);
let e = loop {
let e = 'outer: loop {
match subscription.receive().await {
JsonResult::Notification(n) => {
let mut shell_message =
vec![String::from("Got Block notification from darkfid subscription")];
if n.method != "blockchain.subscribe_blocks" {
shell_sender.send(shell_message).await?;
break Error::UnexpectedJsonRpc(format!(
"Got foreign notification from darkfid: {}",
n.method
@@ -658,12 +675,14 @@ pub async fn subscribe_blocks(
// Verify parameters
if !n.params.is_array() {
shell_sender.send(shell_message).await?;
break Error::UnexpectedJsonRpc(
"Received notification params are not an array".to_string(),
)
}
let params = n.params.get::<Vec<JsonValue>>().unwrap();
if params.is_empty() {
shell_sender.send(shell_message).await?;
break Error::UnexpectedJsonRpc("Notification parameters are empty".to_string())
}
@@ -679,10 +698,11 @@ pub async fn subscribe_blocks(
let lock = drk.read().await;
if block.header.height <= last_scanned_height {
let reset_height = block.header.height.saturating_sub(1);
if let Err(e) = lock.reset_to_height(reset_height) {
return Err(Error::DatabaseError(format!(
if let Err(e) = lock.reset_to_height(reset_height, &mut shell_message) {
shell_sender.send(shell_message).await?;
break 'outer Error::Custom(format!(
"[subscribe_blocks] Wallet state reset failed: {e:?}"
)))
))
}
// Scan genesis again if needed
@@ -690,16 +710,18 @@ pub async fn subscribe_blocks(
let genesis = match lock.get_block_by_height(reset_height).await {
Ok(b) => b,
Err(e) => {
return Err(Error::Custom(format!(
shell_sender.send(shell_message).await?;
break 'outer Error::Custom(format!(
"[subscribe_blocks] RPC client request failed: {e:?}"
)))
))
}
};
let mut scan_cache = lock.scan_cache().await?;
if let Err(e) = lock.scan_block(&mut scan_cache, &genesis).await {
return Err(Error::DatabaseError(format!(
shell_sender.send(shell_message).await?;
break 'outer Error::Custom(format!(
"[subscribe_blocks] Scanning block failed: {e:?}"
)))
))
};
for msg in scan_cache.flush_messages() {
shell_message.push(msg);
@@ -709,9 +731,10 @@ pub async fn subscribe_blocks(
let mut scan_cache = lock.scan_cache().await?;
if let Err(e) = lock.scan_block(&mut scan_cache, &block).await {
return Err(Error::DatabaseError(format!(
shell_sender.send(shell_message).await?;
break 'outer Error::Custom(format!(
"[subscribe_blocks] Scanning block failed: {e:?}"
)))
))
}
for msg in scan_cache.flush_messages() {
shell_message.push(msg);
@@ -735,5 +758,6 @@ pub async fn subscribe_blocks(
}
};
shell_sender.send(vec![format!("[subscribe_blocks] Subscription loop break: {e}")]).await?;
Err(e)
}

View File

@@ -78,30 +78,34 @@ impl Drk {
}
/// Reset the scanned blocks information records in the cache.
pub fn reset_scanned_blocks(&self) -> WalletDbResult<()> {
println!("Resetting scanned blocks");
pub fn reset_scanned_blocks(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting scanned blocks"));
if let Err(e) = self.cache.scanned_blocks.clear() {
println!("[reset_scanned_blocks] Resetting scanned blocks tree failed: {e:?}");
output.push(format!(
"[reset_scanned_blocks] Resetting scanned blocks tree failed: {e:?}"
));
return Err(WalletDbError::GenericError)
}
if let Err(e) = self.cache.state_inverse_diff.clear() {
println!("[reset_scanned_blocks] Resetting state inverse diffs tree failed: {e:?}");
output.push(format!(
"[reset_scanned_blocks] Resetting state inverse diffs tree failed: {e:?}"
));
return Err(WalletDbError::GenericError)
}
println!("Successfully reset scanned blocks");
output.push(String::from("Successfully reset scanned blocks"));
Ok(())
}
/// Reset state to provided block height.
/// If genesis block height(0) was provided, perform a full reset.
pub fn reset_to_height(&self, height: u32) -> WalletDbResult<()> {
println!("Resetting wallet state to block: {height}");
pub fn reset_to_height(&self, height: u32, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(format!("Resetting wallet state to block: {height}"));
// If genesis block height(0) was provided,
// perform a full reset.
if height == 0 {
return self.reset()
return self.reset(output)
}
// Grab last scanned block height
@@ -109,7 +113,9 @@ impl Drk {
// Check if requested height is after it
if last <= height {
println!("Requested block height is greater or equal to last scanned block");
output.push(String::from(
"Requested block height is greater or equal to last scanned block",
));
return Ok(())
}
@@ -117,7 +123,7 @@ impl Drk {
let mut overlay = match CacheOverlay::new(&self.cache) {
Ok(o) => o,
Err(e) => {
println!("[reset_to_height] Creating cache overlay failed: {e:?}");
output.push(format!("[reset_to_height] Creating cache overlay failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
};
@@ -128,70 +134,71 @@ impl Drk {
let inverse_diff = match self.cache.get_state_inverse_diff(&height) {
Ok(d) => d,
Err(e) => {
println!(
output.push(format!(
"[reset_to_height] Retrieving state inverse diff from cache failed: {e:?}"
);
));
return Err(WalletDbError::GenericError)
}
};
// Apply it
if let Err(e) = overlay.0.add_diff(&inverse_diff) {
println!("[reset_to_height] Adding state inverse diff to the cache overlay failed: {e:?}");
output.push(format!("[reset_to_height] Adding state inverse diff to the cache overlay failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
if let Err(e) = overlay.0.apply_diff(&inverse_diff) {
println!("[reset_to_height] Applying state inverse diff to the cache overlay failed: {e:?}");
output.push(format!("[reset_to_height] Applying state inverse diff to the cache overlay failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
// Remove it
if let Err(e) = self.cache.state_inverse_diff.remove(height.to_be_bytes()) {
println!(
output.push(format!(
"[reset_to_height] Removing state inverse diff from the cache failed: {e:?}"
);
));
return Err(WalletDbError::GenericError)
}
// Flush sled
if let Err(e) = self.cache.sled_db.flush() {
println!("[reset_to_height] Flushing cache sled database failed: {e:?}");
output
.push(format!("[reset_to_height] Flushing cache sled database failed: {e:?}"));
return Err(WalletDbError::GenericError)
}
}
// Remove all wallet coins created after the reset height
self.remove_money_coins_after(&height)?;
self.remove_money_coins_after(&height, output)?;
// Unspent all wallet coins spent after the reset height
self.unspent_money_coins_after(&height)?;
self.unspent_money_coins_after(&height, output)?;
// Unfreeze tokens mint authorities frozen after the reset
// height.
self.unfreeze_mint_authorities_after(&height)?;
self.unfreeze_mint_authorities_after(&height, output)?;
// Unconfirm DAOs minted after the reset height
self.unconfirm_daos_after(&height)?;
self.unconfirm_daos_after(&height, output)?;
// Unconfirm DAOs proposals minted after the reset height
self.unconfirm_dao_proposals_after(&height)?;
self.unconfirm_dao_proposals_after(&height, output)?;
// Reset execution information for DAOs proposals executed
// after the reset height.
self.unexec_dao_proposals_after(&height)?;
self.unexec_dao_proposals_after(&height, output)?;
// Remove all DAOs proposals votes created after the reset
// height.
self.remove_dao_votes_after(&height)?;
self.remove_dao_votes_after(&height, output)?;
// Unfreeze all contracts frozen after the reset height
self.unfreeze_deploy_authorities_after(&height)?;
self.unfreeze_deploy_authorities_after(&height, output)?;
// Set reverted status to all transactions executed after reset
// height.
self.revert_transactions_after(&height)?;
self.revert_transactions_after(&height, output)?;
println!("Successfully reset wallet state");
output.push(String::from("Successfully reset wallet state"));
Ok(())
}
}

View File

@@ -287,11 +287,11 @@ impl Drk {
}
/// Inspect and verify a given swap (half or full) transaction
pub async fn inspect_swap(&self, bytes: Vec<u8>) -> Result<()> {
pub async fn inspect_swap(&self, bytes: Vec<u8>, output: &mut Vec<String>) -> Result<()> {
// First we check if its a partial swap
if let Ok(partial) = deserialize_async::<PartialSwapData>(&bytes).await {
// Inspect the PartialSwapData
println!("{partial}");
output.push(format!("{partial}"));
return Ok(())
}
@@ -307,23 +307,23 @@ impl Drk {
// We're inspecting a full transaction
if tx.calls.len() != 1 {
eprintln!(
output.push(format!(
"Found {} contract calls in the transaction, there should be 1",
tx.calls.len()
);
));
return insection_error
}
let params: MoneyTransferParamsV1 = deserialize_async(&tx.calls[0].data.data[1..]).await?;
println!("Parameters:\n{params:#?}");
output.push(format!("Parameters:\n{params:#?}"));
if params.inputs.len() != 2 {
eprintln!("Found {} inputs, there should be 2", params.inputs.len());
output.push(format!("Found {} inputs, there should be 2", params.inputs.len()));
return insection_error
}
if params.outputs.len() != 2 {
eprintln!("Found {} outputs, there should be 2", params.outputs.len());
output.push(format!("Found {} outputs, there should be 2", params.outputs.len()));
return insection_error
}
@@ -331,17 +331,18 @@ impl Drk {
let secret_keys = self.get_money_secrets().await?;
let mut skey: Option<SecretKey> = None;
let mut note: Option<MoneyNote> = None;
let mut output_idx = 0;
let mut param_output_idx = 0;
for output in &params.outputs {
println!("Trying to decrypt note in output {output_idx}");
for param_output in &params.outputs {
output.push(format!("Trying to decrypt note in output {param_output_idx}"));
for secret in &secret_keys {
if let Ok(d_note) = output.note.decrypt::<MoneyNote>(secret) {
if let Ok(d_note) = param_output.note.decrypt::<MoneyNote>(secret) {
let s: SecretKey = deserialize_async(&d_note.memo).await?;
skey = Some(s);
note = Some(d_note);
println!("Successfully decrypted and found an ephemeral secret");
output
.push(String::from("Successfully decrypted and found an ephemeral secret"));
break
}
}
@@ -350,20 +351,20 @@ impl Drk {
break
}
output_idx += 1;
param_output_idx += 1;
}
let Some(note) = note else {
eprintln!("Error: Could not decrypt notes of either output");
output.push(String::from("Error: Could not decrypt notes of either output"));
return insection_error
};
println!(
"Output[{output_idx}] value: {} ({})",
output.push(format!(
"Output[{param_output_idx}] value: {} ({})",
note.value,
encode_base10(note.value, BALANCE_BASE10_DECIMALS)
);
println!("Output[{output_idx}] token ID: {}", note.token_id);
));
output.push(format!("Output[{param_output_idx}] token ID: {}", note.token_id));
let skey = skey.unwrap();
let (pub_x, pub_y) = PublicKey::from_secret(skey).xy();
@@ -375,35 +376,43 @@ impl Drk {
note.coin_blind.inner(),
]));
if coin == params.outputs[output_idx].coin {
println!("Output[{output_idx}] coin matches decrypted note metadata");
if coin == params.outputs[param_output_idx].coin {
output.push(format!("Output[{param_output_idx}] coin matches decrypted note metadata"));
} else {
eprintln!("Error: Output[{output_idx}] coin does not match note metadata");
output.push(format!(
"Error: Output[{param_output_idx}] coin does not match note metadata"
));
return insection_error
}
let valcom = pedersen_commitment_u64(note.value, note.value_blind);
let tokcom = poseidon_hash([note.token_id.inner(), note.token_blind.inner()]);
if valcom != params.outputs[output_idx].value_commit {
eprintln!("Error: Output[{output_idx}] value commitment does not match note metadata");
if valcom != params.outputs[param_output_idx].value_commit {
output.push(format!(
"Error: Output[{param_output_idx}] value commitment does not match note metadata"
));
return insection_error
}
if tokcom != params.outputs[output_idx].token_commit {
eprintln!("Error: Output[{output_idx}] token commitment does not match note metadata");
if tokcom != params.outputs[param_output_idx].token_commit {
output.push(format!(
"Error: Output[{param_output_idx}] token commitment does not match note metadata"
));
return insection_error
}
println!("Value and token commitments match decrypted note metadata");
output.push(String::from("Value and token commitments match decrypted note metadata"));
// Verify that the output commitments match the other input commitments
match output_idx {
// Verify that the param output commitments match the other input commitments
match param_output_idx {
0 => {
if valcom != params.inputs[1].value_commit ||
tokcom != params.inputs[1].token_commit
{
eprintln!("Error: Value/Token commits of output[0] do not match input[1]");
output.push(String::from(
"Error: Value/Token commits of output[0] do not match input[1]",
));
return insection_error
}
}
@@ -411,14 +420,16 @@ impl Drk {
if valcom != params.inputs[0].value_commit ||
tokcom != params.inputs[0].token_commit
{
eprintln!("Error: Value/Token commits of output[1] do not match input[0]");
output.push(String::from(
"Error: Value/Token commits of output[1] do not match input[0]",
));
return insection_error
}
}
_ => unreachable!(),
}
println!("Found matching pedersen commitments for outputs and inputs");
output.push(String::from("Found matching pedersen commitments for outputs and inputs"));
Ok(())
}
@@ -475,7 +486,6 @@ impl Drk {
}
if !found {
eprintln!("Error: Failed to decrypt note with any of our secret keys");
return Err(Error::Custom(
"Failed to decrypt note with any of our secret keys".to_string(),
))

View File

@@ -175,22 +175,26 @@ impl Drk {
}
/// Reset all token mint authorities frozen status in the wallet.
pub fn reset_mint_authorities(&self) -> WalletDbResult<()> {
println!("Resetting mint authorities frozen status");
pub fn reset_mint_authorities(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting mint authorities frozen status"));
let query = format!(
"UPDATE {} SET {} = 0, {} = NULL;",
*MONEY_TOKENS_TABLE, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_FREEZE_HEIGHT
);
self.wallet.exec_sql(&query, &[])?;
println!("Successfully reset mint authorities frozen status");
output.push(String::from("Successfully reset mint authorities frozen status"));
Ok(())
}
/// Remove token mint authorities frozen status in the wallet that
/// where frozen after provided height.
pub fn unfreeze_mint_authorities_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Resetting mint authorities frozen status after: {height}");
pub fn unfreeze_mint_authorities_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Resetting mint authorities frozen status after: {height}"));
let query = format!(
"UPDATE {} SET {} = 0, {} = NULL WHERE {} > ?1;",
*MONEY_TOKENS_TABLE,
@@ -199,7 +203,7 @@ impl Drk {
MONEY_TOKENS_COL_FREEZE_HEIGHT
);
self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
println!("Successfully reset mint authorities frozen status");
output.push(String::from("Successfully reset mint authorities frozen status"));
Ok(())
}

View File

@@ -172,19 +172,23 @@ impl Drk {
}
/// Reset the transaction history records in the wallet.
pub fn reset_tx_history(&self) -> WalletDbResult<()> {
println!("Resetting transactions history");
pub fn reset_tx_history(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
output.push(String::from("Resetting transactions history"));
let query = format!("DELETE FROM {WALLET_TXS_HISTORY_TABLE};");
self.wallet.exec_sql(&query, &[])?;
println!("Successfully reset transactions history");
output.push(String::from("Successfully reset transactions history"));
Ok(())
}
/// Set reverted status to the transaction history records in the
/// wallet that where executed after provided height.
pub fn revert_transactions_after(&self, height: &u32) -> WalletDbResult<()> {
println!("Reverting transactions history after: {height}");
pub fn revert_transactions_after(
&self,
height: &u32,
output: &mut Vec<String>,
) -> WalletDbResult<()> {
output.push(format!("Reverting transactions history after: {height}"));
let query = format!(
"UPDATE {} SET {} = 'Reverted', {} = NULL WHERE {} > ?1;",
WALLET_TXS_HISTORY_TABLE,
@@ -193,7 +197,7 @@ impl Drk {
WALLET_TXS_HISTORY_BLOCK_HEIGHT
);
self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
println!("Successfully reverted transactions history");
output.push(String::from("Successfully reverted transactions history"));
Ok(())
}