mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
drk/interactive: previous command output as next command input
This commit is contained in:
@@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ¶ms.outputs {
|
||||
println!("Trying to decrypt note in output {output_idx}");
|
||||
for param_output in ¶ms.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(),
|
||||
))
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user