Skip to content

Commit

Permalink
Edited bip353_parse to use it in the common version of the sdk. Edite…
Browse files Browse the repository at this point in the history
…d to accept the dns_resolver from outside
  • Loading branch information
lorenzoronzani committed Dec 19, 2024
1 parent 3d27ed6 commit d5e2a19
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 37 deletions.
74 changes: 43 additions & 31 deletions libs/sdk-common/src/input_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ use ::bip21::Uri;
use anyhow::{anyhow, Context, Result};
use bitcoin::bech32;
use bitcoin::bech32::FromBase32;
use hickory_resolver::name_server::GenericConnector;
use hickory_resolver::AsyncResolver;
use log::error;
use percent_encoding::NON_ALPHANUMERIC;
use regex::Regex;
use serde::{Deserialize, Serialize};
use LnUrlRequestData::*;

use trust_dns_resolver::config::*;
use trust_dns_resolver::TokioAsyncResolver;
use hickory_resolver::name_server::*;

use crate::prelude::*;

const USER_BITCOIN_PAYMENT_PREFIX: &str = "user._bitcoin-payment";
const BOLT12_PREFIX: &str = "lno=";
const LNURL_PAY_PREFIX: &str = "lnurl=";

/// Parses generic user input, typically pasted from clipboard or scanned from a QR.
Expand Down Expand Up @@ -186,12 +188,43 @@ const LNURL_PAY_PREFIX: &str = "lnurl=";
/// }
/// }
/// ```
pub async fn parse(
input: &str,
external_input_parsers: Option<&[ExternalInputParser]>,
dns_resolver: Option<&AsyncResolver<GenericConnector<TokioRuntimeProvider>>>,
) -> Result<InputType> {
let mut input = input.trim();

async fn bip353_parse(input: &str) -> Option<String> {
if let Some((local_part, domain)) = input.split_once('@') {
let dns_resolver =
TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default());
// Try to parse the destination as a bip353 address.
let input_str = if let Some(resolver) = dns_resolver {
match bip353_parse(input, resolver).await {
Some(value) => value,
None => input.to_string(),
}
} else {
input.to_string()
};

println!("Input {}, input_str {}", input, input_str);

input = input_str.as_str();

if let Ok(input_type) = parse_core(input).await {
return Ok(input_type);
}

if let Some(external_input_parsers) = external_input_parsers {
return parse_external(input, external_input_parsers).await;
}

Err(anyhow!("Unrecognized input type"))
}

async fn bip353_parse(
input: &str,
dns_resolver: &AsyncResolver<GenericConnector<TokioRuntimeProvider>>,
) -> Option<String> {
if let Some((local_part, domain)) = input.split_once('@') {
let dns_name = format!("{}.{}.{}", local_part, USER_BITCOIN_PAYMENT_PREFIX, domain);

// Query for TXT records of a domain
Expand All @@ -209,6 +242,10 @@ async fn bip353_parse(input: &str) -> Option<String> {
// Decode TXT data
match String::from_utf8(txt_data) {
Ok(decoded) => {
if let Some((_, bolt12_address)) = decoded.split_once(BOLT12_PREFIX) {
return Some(bolt12_address.to_string());
}

if let Some((_, lnurl)) = decoded.split_once(LNURL_PAY_PREFIX) {
return Some(lnurl.to_string());
}
Expand All @@ -222,31 +259,6 @@ async fn bip353_parse(input: &str) -> Option<String> {
None
}

pub async fn parse(
input: &str,
external_input_parsers: Option<&[ExternalInputParser]>,
) -> Result<InputType> {
let mut input = input.trim();

// Try to parse the destination as a bip353 address.
let input_str = match bip353_parse(input).await {
Some(value) => value,
None => input.to_string(),
};

input = input_str.as_str();

if let Ok(input_type) = parse_core(input).await {
return Ok(input_type);
}

if let Some(external_input_parsers) = external_input_parsers {
return parse_external(input, external_input_parsers).await;
}

Err(anyhow!("Unrecognized input type"))
}

/// Core parse implementation
async fn parse_core(input: &str) -> Result<InputType> {
// Covers BIP 21 URIs and simple onchain BTC addresses (which are valid BIP 21 with the 'bitcoin:' prefix)
Expand Down
2 changes: 1 addition & 1 deletion libs/sdk-core/src/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ pub fn parse_invoice(invoice: String) -> Result<LNInvoice> {
}

pub fn parse_input(input: String) -> Result<InputType> {
block_on(async { parse(&input, None).await })
block_on(async { parse(&input, None, None).await })
}

/* Payment API's */
Expand Down
12 changes: 8 additions & 4 deletions tools/sdk-cli/src/command_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ use rustyline::hint::HistoryHinter;
use rustyline::Editor;
use rustyline::{Completer, Helper, Hinter, Validator};

use breez_sdk_core::hickory_resolver::name_server::{GenericConnector, TokioRuntimeProvider};
use breez_sdk_core::hickory_resolver::AsyncResolver;

static BREEZ_SERVICES: OnceCell<Arc<BreezServices>> = OnceCell::new();

fn sdk() -> Result<Arc<BreezServices>> {
Expand Down Expand Up @@ -72,6 +75,7 @@ pub(crate) async fn handle_command(
rl: &mut Editor<CliHelper, DefaultHistory>,
persistence: &CliPersistence,
command: Commands,
dns_resolver: &AsyncResolver<GenericConnector<TokioRuntimeProvider>>,
) -> Result<String, Error> {
match command {
Commands::SetAPIKey { key } => {
Expand Down Expand Up @@ -124,7 +128,7 @@ pub(crate) async fn handle_command(
sdk()?.sync().await?;
Ok("Sync finished successfully".to_string())
}
Commands::Parse { input } => parse(&input, None)
Commands::Parse { input } => parse(&input, None, Some(dns_resolver))
.await
.map(|res| serde_json::to_string_pretty(&res))?
.map_err(|e| e.into()),
Expand Down Expand Up @@ -466,7 +470,7 @@ pub(crate) async fn handle_command(
label,
validate_success_url,
use_trampoline,
} => match parse(&lnurl, None).await? {
} => match parse(&lnurl, None, Some(dns_resolver)).await? {
LnUrlPay { data: pd } => {
let prompt = format!(
"Amount to pay in millisatoshi (min {} msat, max {} msat: ",
Expand Down Expand Up @@ -494,7 +498,7 @@ pub(crate) async fn handle_command(
_ => Err(anyhow!("Invalid input")),
},
Commands::LnurlWithdraw { lnurl } => {
match parse(&lnurl, None).await? {
match parse(&lnurl, None, Some(dns_resolver)).await? {
LnUrlWithdraw { data: wd } => {
info!("Endpoint description: {}", wd.default_description);

Expand Down Expand Up @@ -536,7 +540,7 @@ pub(crate) async fn handle_command(
Commands::LnurlAuth { lnurl } => {
let lnurl_endpoint = lnurl.trim();

match parse(lnurl_endpoint, None).await? {
match parse(lnurl_endpoint, None, Some(dns_resolver)).await? {
LnUrlAuth { data: ad } => {
let auth_res = sdk()?.lnurl_auth(ad).await?;
serde_json::to_string_pretty(&auth_res).map_err(|e| e.into())
Expand Down
9 changes: 8 additions & 1 deletion tools/sdk-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ mod persist;

use crate::command_handlers::CliHelper;
use anyhow::{anyhow, ensure, Result};
use breez_sdk_core::hickory_resolver::config::{ResolverConfig, ResolverOpts};
use breez_sdk_core::hickory_resolver::TokioAsyncResolver;
use breez_sdk_core::BreezServices;
use clap::Parser;
use command_handlers::handle_command;
Expand Down Expand Up @@ -40,6 +42,11 @@ async fn main() -> Result<()> {
info!("No previous history.");
}

let mut dns_resolvers_opts = ResolverOpts::default();
dns_resolvers_opts.validate = true;

let dns_resolver = TokioAsyncResolver::tokio(ResolverConfig::default(), dns_resolvers_opts);

loop {
let readline = rl.readline("sdk> ");
match readline {
Expand All @@ -52,7 +59,7 @@ async fn main() -> Result<()> {
println!("{}", cli_res.unwrap_err());
continue;
}
let res = handle_command(rl, &persistence, cli_res.unwrap()).await;
let res = handle_command(rl, &persistence, cli_res.unwrap(), &dns_resolver).await;
show_results(res);
continue;
}
Expand Down

0 comments on commit d5e2a19

Please sign in to comment.