[cli] Make the REPL return JSON

This commit is contained in:
Alekos Filini 2020-08-15 20:16:34 +02:00
parent 5777431135
commit 21318eb940
No known key found for this signature in database
GPG Key ID: 5E8AFC3034FDFA4F
2 changed files with 48 additions and 96 deletions

View File

@ -101,11 +101,9 @@ fn main() {
continue; continue;
} }
if let Some(s) = let result =
cli::handle_matches(&Arc::clone(&wallet), matches.unwrap()).unwrap() cli::handle_matches(&Arc::clone(&wallet), matches.unwrap()).unwrap();
{ println!("{}", serde_json::to_string_pretty(&result).unwrap());
println!("{}", s);
}
} }
Err(ReadlineError::Interrupted) => continue, Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break, Err(ReadlineError::Eof) => break,
@ -118,8 +116,7 @@ fn main() {
// rl.save_history("history.txt").unwrap(); // rl.save_history("history.txt").unwrap();
} else { } else {
if let Some(s) = cli::handle_matches(&wallet, matches).unwrap() { let result = cli::handle_matches(&wallet, matches).unwrap();
println!("{}", s); println!("{}", serde_json::to_string_pretty(&result).unwrap());
}
} }
} }

View File

@ -7,7 +7,7 @@ use clap::{App, Arg, ArgMatches, SubCommand};
use log::{debug, error, info, trace, LevelFilter}; use log::{debug, error, info, trace, LevelFilter};
use bitcoin::consensus::encode::{deserialize, serialize, serialize_hex}; use bitcoin::consensus::encode::{deserialize, serialize, serialize_hex};
use bitcoin::hashes::hex::{FromHex, ToHex}; use bitcoin::hashes::hex::FromHex;
use bitcoin::util::psbt::PartiallySignedTransaction; use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::{Address, OutPoint, Txid}; use bitcoin::{Address, OutPoint, Txid};
@ -337,42 +337,26 @@ pub fn add_global_flags<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
pub fn handle_matches<C, D>( pub fn handle_matches<C, D>(
wallet: &Wallet<C, D>, wallet: &Wallet<C, D>,
matches: ArgMatches<'_>, matches: ArgMatches<'_>,
) -> Result<Option<String>, Error> ) -> Result<serde_json::Value, Error>
where where
C: crate::blockchain::OnlineBlockchain, C: crate::blockchain::OnlineBlockchain,
D: crate::database::BatchDatabase, D: crate::database::BatchDatabase,
{ {
if let Some(_sub_matches) = matches.subcommand_matches("get_new_address") { if let Some(_sub_matches) = matches.subcommand_matches("get_new_address") {
Ok(Some(format!("{}", wallet.get_new_address()?))) Ok(json!({
"address": wallet.get_new_address()?
}))
} else if let Some(_sub_matches) = matches.subcommand_matches("sync") { } else if let Some(_sub_matches) = matches.subcommand_matches("sync") {
maybe_await!(wallet.sync(log_progress(), None))?; maybe_await!(wallet.sync(log_progress(), None))?;
Ok(None) Ok(json!({}))
} else if let Some(_sub_matches) = matches.subcommand_matches("list_unspent") { } else if let Some(_sub_matches) = matches.subcommand_matches("list_unspent") {
let mut res = String::new(); Ok(serde_json::to_value(&wallet.list_unspent()?)?)
for utxo in wallet.list_unspent()? {
res += &format!("{} value {} SAT\n", utxo.outpoint, utxo.txout.value);
}
Ok(Some(res))
} else if let Some(_sub_matches) = matches.subcommand_matches("list_transactions") { } else if let Some(_sub_matches) = matches.subcommand_matches("list_transactions") {
let mut res = String::new(); Ok(serde_json::to_value(&wallet.list_transactions(false)?)?)
for crate::types::TransactionDetails {
txid,
sent,
received,
height,
..
} in wallet.list_transactions(false)?
{
res += &format!(
"{} - sent {}, received {} - height: {:?}\n",
txid, sent, received, height
);
}
Ok(Some(res))
} else if let Some(_sub_matches) = matches.subcommand_matches("get_balance") { } else if let Some(_sub_matches) = matches.subcommand_matches("get_balance") {
Ok(Some(format!("{} SAT", wallet.get_balance()?))) Ok(json!({
"satoshi": wallet.get_balance()?
}))
} else if let Some(sub_matches) = matches.subcommand_matches("create_tx") { } else if let Some(sub_matches) = matches.subcommand_matches("create_tx") {
let addressees = sub_matches let addressees = sub_matches
.values_of("to") .values_of("to")
@ -414,12 +398,11 @@ where
tx_builder = tx_builder.policy_path(policy); tx_builder = tx_builder.policy_path(policy);
} }
let result = wallet.create_tx(tx_builder)?; let (psbt, details) = wallet.create_tx(tx_builder)?;
Ok(Some(format!( Ok(json!({
"{:#?}\nPSBT: {}", "psbt": base64::encode(&serialize(&psbt)),
result.1, "details": details,
base64::encode(&serialize(&result.0)) }))
)))
} else if let Some(sub_matches) = matches.subcommand_matches("bump_fee") { } else if let Some(sub_matches) = matches.subcommand_matches("bump_fee") {
let txid = Txid::from_str(sub_matches.value_of("txid").unwrap()) let txid = Txid::from_str(sub_matches.value_of("txid").unwrap())
.map_err(|s| Error::Generic(s.to_string()))?; .map_err(|s| Error::Generic(s.to_string()))?;
@ -448,32 +431,21 @@ where
tx_builder = tx_builder.unspendable(unspendable); tx_builder = tx_builder.unspendable(unspendable);
} }
let result = wallet.bump_fee(&txid, tx_builder)?; let (psbt, details) = wallet.bump_fee(&txid, tx_builder)?;
Ok(Some(format!( Ok(json!({
"{:#?}\nPSBT: {}", "psbt": base64::encode(&serialize(&psbt)),
result.1, "details": details,
base64::encode(&serialize(&result.0)) }))
)))
} else if let Some(_sub_matches) = matches.subcommand_matches("policies") { } else if let Some(_sub_matches) = matches.subcommand_matches("policies") {
Ok(Some(format!( Ok(json!({
"External: {}\nInternal:{}", "external": wallet.policies(ScriptType::External)?,
serde_json::to_string(&wallet.policies(ScriptType::External)?).unwrap(), "internal": wallet.policies(ScriptType::Internal)?,
serde_json::to_string(&wallet.policies(ScriptType::Internal)?).unwrap(), }))
)))
} else if let Some(_sub_matches) = matches.subcommand_matches("public_descriptor") { } else if let Some(_sub_matches) = matches.subcommand_matches("public_descriptor") {
let external = match wallet.public_descriptor(ScriptType::External)? { Ok(json!({
Some(desc) => format!("{}", desc), "external": wallet.public_descriptor(ScriptType::External)?.map(|d| d.to_string()),
None => "<NONE>".into(), "internal": wallet.public_descriptor(ScriptType::Internal)?.map(|d| d.to_string()),
}; }))
let internal = match wallet.public_descriptor(ScriptType::Internal)? {
Some(desc) => format!("{}", desc),
None => "<NONE>".into(),
};
Ok(Some(format!(
"External: {}\nInternal:{}",
external, internal
)))
} else if let Some(sub_matches) = matches.subcommand_matches("sign") { } else if let Some(sub_matches) = matches.subcommand_matches("sign") {
let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap(); let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap();
let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap(); let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
@ -481,16 +453,10 @@ where
.value_of("assume_height") .value_of("assume_height")
.and_then(|s| Some(s.parse().unwrap())); .and_then(|s| Some(s.parse().unwrap()));
let (psbt, finalized) = wallet.sign(psbt, assume_height)?; let (psbt, finalized) = wallet.sign(psbt, assume_height)?;
Ok(json!({
let mut res = String::new(); "psbt": base64::encode(&serialize(&psbt)),
"is_finalized": finalized,
res += &format!("PSBT: {}\n", base64::encode(&serialize(&psbt))); }))
res += &format!("Finalized: {}", finalized);
if finalized {
res += &format!("\nExtracted: {}", serialize_hex(&psbt.extract_tx()));
}
Ok(Some(res))
} else if let Some(sub_matches) = matches.subcommand_matches("broadcast") { } else if let Some(sub_matches) = matches.subcommand_matches("broadcast") {
let tx = if sub_matches.value_of("psbt").is_some() { let tx = if sub_matches.value_of("psbt").is_some() {
let psbt = base64::decode(&sub_matches.value_of("psbt").unwrap()).unwrap(); let psbt = base64::decode(&sub_matches.value_of("psbt").unwrap()).unwrap();
@ -504,16 +470,13 @@ where
}; };
let txid = maybe_await!(wallet.broadcast(tx))?; let txid = maybe_await!(wallet.broadcast(tx))?;
Ok(json!({ "txid": txid }))
Ok(Some(format!("TXID: {}", txid)))
} else if let Some(sub_matches) = matches.subcommand_matches("extract_psbt") { } else if let Some(sub_matches) = matches.subcommand_matches("extract_psbt") {
let psbt = base64::decode(&sub_matches.value_of("psbt").unwrap()).unwrap(); let psbt = base64::decode(&sub_matches.value_of("psbt").unwrap()).unwrap();
let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap(); let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
Ok(json!({
Ok(Some(format!( "raw_tx": serialize_hex(&psbt.extract_tx()),
"TX: {}", }))
serialize(&psbt.extract_tx()).to_hex()
)))
} else if let Some(sub_matches) = matches.subcommand_matches("finalize_psbt") { } else if let Some(sub_matches) = matches.subcommand_matches("finalize_psbt") {
let psbt = base64::decode(&sub_matches.value_of("psbt").unwrap()).unwrap(); let psbt = base64::decode(&sub_matches.value_of("psbt").unwrap()).unwrap();
let mut psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap(); let mut psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
@ -523,15 +486,10 @@ where
.and_then(|s| Some(s.parse().unwrap())); .and_then(|s| Some(s.parse().unwrap()));
let finalized = wallet.finalize_psbt(&mut psbt, assume_height)?; let finalized = wallet.finalize_psbt(&mut psbt, assume_height)?;
Ok(json!({
let mut res = String::new(); "psbt": base64::encode(&serialize(&psbt)),
res += &format!("PSBT: {}\n", base64::encode(&serialize(&psbt))); "is_finalized": finalized,
res += &format!("Finalized: {}", finalized); }))
if finalized {
res += &format!("\nExtracted: {}", serialize_hex(&psbt.extract_tx()));
}
Ok(Some(res))
} else if let Some(sub_matches) = matches.subcommand_matches("combine_psbt") { } else if let Some(sub_matches) = matches.subcommand_matches("combine_psbt") {
let mut psbts = sub_matches let mut psbts = sub_matches
.values_of("psbt") .values_of("psbt")
@ -555,11 +513,8 @@ where
}, },
)?; )?;
Ok(Some(format!( Ok(json!({ "psbt": base64::encode(&serialize(&final_psbt)) }))
"PSBT: {}",
base64::encode(&serialize(&final_psbt))
)))
} else { } else {
Ok(None) Ok(serde_json::Value::Null)
} }
} }