Skip to content

Commit

Permalink
Merge branch 'trunk' into ok300-chain-swap-refund-check-all-utoxs
Browse files Browse the repository at this point in the history
  • Loading branch information
ok300 committed Oct 25, 2024
2 parents 368d31c + e30a43f commit e482332
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 5 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ panic = "abort"
[dependencies]
serde = { version = "1.0.0", features = ["derive"] }
serde_json = "1.0.0"
ureq = { version = "2.9.7", features = ["json", "native-tls"] }
ureq = { version = "2.5.0", features = ["json", "native-tls"] }
bip39 = "2.0.0"
electrum-client = "0.19.0"
bitcoin = {version = "0.31.2", features = ["rand", "base64", "rand-std"]}
Expand All @@ -34,7 +34,7 @@ log = "^0.4"
env_logger = "0.7"
native-tls = "0.2.11"
hex = "0.4"

lnurl-rs = "0.8.0"
[patch.crates-io]
secp256k1-zkp = {git = "https://github.com/BlockstreamResearch/rust-secp256k1-zkp.git", rev = "60e631c24588a0c9e271badd61959294848c665d"}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub use bitcoin;
pub use electrum_client;
pub use elements;
pub use lightning_invoice;
pub use lnurl;

// Re-export relevant structs under boltz_client::StructName for simplicity
pub use bitcoin::{
Expand Down
2 changes: 1 addition & 1 deletion src/swaps/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ impl BtcSwapTx {
/// Use this before calling drain to help calculate the absolute fees.
/// Multiply the size by the fee_rate to get the absolute fees.
pub fn size(&self, keys: &Keypair, preimage: &Preimage) -> Result<usize, Error> {
let dummy_abs_fee = 5_000;
let dummy_abs_fee = 0;
// Can only calculate non-coperative claims
let tx = match self.kind {
SwapTxKind::Claim => self.sign_claim(keys, preimage, dummy_abs_fee, None)?,
Expand Down
2 changes: 1 addition & 1 deletion src/swaps/liquid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ impl LBtcSwapTx {
/// Use this before calling drain to help calculate the absolute fees.
/// Multiply the size by the fee_rate to get the absolute fees.
pub fn size(&self, keys: &Keypair, preimage: &Preimage) -> Result<usize, Error> {
let dummy_abs_fee = Amount::from_sat(5_000);
let dummy_abs_fee = Amount::from_sat(0);
let tx = match self.kind {
SwapTxKind::Claim => self.sign_claim(keys, preimage, dummy_abs_fee, None)?, // TODO: Hardcode cooperative spend size
SwapTxKind::Refund => self.sign_refund(keys, dummy_abs_fee, None)?,
Expand Down
163 changes: 163 additions & 0 deletions src/util/lnurl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use crate::error::Error;
use lightning_invoice::Bolt11Invoice;
use lnurl::lightning_address::LightningAddress;
use lnurl::pay::LnURLPayInvoice;
use lnurl::withdraw::WithdrawalResponse;
use lnurl::{lnurl::LnUrl, Builder, LnUrlResponse};
use std::cmp::max;
use std::str::FromStr;

pub fn validate_lnurl(string: &str) -> bool {
let string = string.to_lowercase();
match LnUrl::from_str(&string) {
Ok(lnurl) => true,
Err(_) => match LightningAddress::from_str(&string) {
Ok(lightning_address) => true,
Err(_) => false,
},
}
}

pub fn fetch_invoice(address: &str, amount_msats: u64) -> Result<String, Error> {
let address = address.to_lowercase();
let lnurl = match LnUrl::from_str(&address) {
Ok(lnurl) => lnurl,
Err(_) => match LightningAddress::from_str(&address) {
Ok(lightning_address) => lightning_address.lnurl(),
Err(_) => return Err(Error::Generic("Not a valid LnUrl or LnAddress".to_string())),
},
};

let client = Builder::default()
.build_blocking()
.map_err(|e| Error::Generic(e.to_string()))?;
let res = client
.make_request(&lnurl.url)
.map_err(|e| Error::HTTP(e.to_string()))?;

match res {
LnUrlResponse::LnUrlPayResponse(pay) => {
let pay_result = client
.get_invoice(&pay, amount_msats, None, None)
.map_err(|e| Error::HTTP(e.to_string()))?;
let invoice =
Bolt11Invoice::from_str(pay_result.invoice()).map_err(|e| Error::Bolt11(e))?;

if invoice.amount_milli_satoshis() != Some(amount_msats) {
return Err(Error::Generic(
"Invoice amount doesn't match requested amount".to_string(),
));
}

Ok(pay_result.invoice().to_string())
}
_ => Err(Error::Generic("Unexpected response type".to_string())),
}
}

pub fn create_withdraw_response(voucher: &str) -> Result<WithdrawalResponse, Error> {
let lnurl = LnUrl::from_str(&*voucher.to_lowercase())
.map_err(|_| Error::Generic("Invalid LNURL".to_string()))?;

let client = Builder::default()
.build_blocking()
.map_err(|e| Error::Generic(e.to_string()))?;

let res = client
.make_request(&lnurl.url)
.map_err(|e| Error::HTTP(e.to_string()))?;

match res {
LnUrlResponse::LnUrlWithdrawResponse(withdraw) => Ok(withdraw),
_ => Err(Error::Generic("Unexpected response type".to_string())),
}
}

pub fn process_withdrawal(withdraw: &WithdrawalResponse, invoice: &str) -> Result<(), Error> {
let client = Builder::default()
.build_blocking()
.map_err(|e| Error::Generic(e.to_string()))?;

let withdraw_result = client
.do_withdrawal(withdraw, invoice)
.map_err(|e| Error::HTTP(e.to_string()))?;

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

fn test_address(address: &str, amount_msats: u64, format: &str) {
let result = fetch_invoice(address, amount_msats);

match result {
Ok(invoice) => {
assert!(!invoice.is_empty(), "Invoice should not be empty");
assert!(
invoice.starts_with("lnbc"),
"Invoice should start with 'lnbc'"
);
println!("Successfully fetched invoice, format : {}", format)
}
Err(e) => {
println!("Error occured with {} format: {}", format, e.message());
}
}
}

#[test]
fn test_fetch_invoice() {
let amount_msats = 100000;
let lnurl = "lnurl1dp68gurn8ghj7um9wfmxjcm99e3k7mf0v9cxj0m385ekvcenxc6r2c35xvukxefcv5mkvv34x5ekzd3ev56nyd3hxqurzepexejxxepnxscrvwfnv9nxzcn9xq6xyefhvgcxxcmyxymnserxfq5fns";
let uppercase_lnurl = lnurl.to_uppercase();
assert!(validate_lnurl(lnurl));
test_address(lnurl, amount_msats, "LNURL");
test_address(&uppercase_lnurl, amount_msats, "LNURL");

let email_lnurl = "[email protected]";
assert!(validate_lnurl(email_lnurl));
test_address(email_lnurl, amount_msats, "Lightning Address");
}

#[ignore = "Requires using an new lnurl-w voucher and invoice to match the max_withdrawble amount"]
#[test]
fn test_process_withdrawal() {
let voucher = "LNURL1DP68GURN8GHJ7ER9D4HJUMRWVF5HGUEWVDHK6TMHD96XSERJV9MJ7CTSDYHHVVF0D3H82UNV9AVYS6ZV899XS4J6WFYRV6Z9TQU4GUT9VF48SWY20AR";
let invoice = "lnbc4u1pnsywcypp5eamm4c3v42vlyr0asmt55muv02zusjp2dy7j6e3kuz5vv3cuyj6scqpjsp56hujjsj4r76gp9gk6y435rz99682uxjx924a06wwqm0av6ezxepq9q7sqqqqqqqqqqqqqqqqqqqsqqqqqysgqdqqmqz9gxqyjw5qrzjqwryaup9lh50kkranzgcdnn2fgvx390wgj5jd07rwr3vxeje0glcllm8u4a8gvusysqqqqlgqqqqqeqqjqtgxt57vzea9xaygxu806xf7w5872n737ptuc6al0plf3544a2f5y2e42j9qv7gvkqkn9k2yxzmew6rr40z2gyq9nu8atj2yt4dlfm3gpjevcgu";
assert!(validate_lnurl(voucher));
let withdraw_response = match create_withdraw_response(voucher) {
Ok(response) => response,
Err(e) => {
println!("Failed to create withdraw response: {:?}", e);
return;
}
};

let invoice_amount = match Bolt11Invoice::from_str(invoice) {
Ok(invoice) => invoice.amount_milli_satoshis(),
Err(e) => {
println!("Failed to parse invoice: {:?}", e);
return;
}
};

assert_eq!(
invoice_amount,
Option::from(withdraw_response.max_withdrawable),
"Invoice of {:?} doesn't match with withdrawable {} sats",
invoice_amount,
withdraw_response.max_withdrawable
);
println!(
"Successfully created withdraw response{:?}",
withdraw_response
);
let result = process_withdrawal(&withdraw_response, invoice);

assert!(result.is_ok(), "Withdrawal failed: {:?}", result.err());

println!("Withdrawal test passed successfully");
}
}
1 change: 1 addition & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use lightning_invoice::{Bolt11Invoice, RouteHintHop};
use crate::{error::Error, network::electrum::ElectrumConfig};

pub mod ec;
pub mod lnurl;
pub mod secrets;

/// Setup function that will only run once, even if called multiple times.
Expand Down
2 changes: 1 addition & 1 deletion tests/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn bitcoin_v2_submarine() {
};

// Set a new invoice string and refund address for each test.
let invoice = "lntb510u1pnpuwgxpp5arh2aw92wv5vxmndm0704zu2qhj6k8xhg38dlylt2atcc3vcs88qdqgdverzvm2xqyjw5qcqp2sp52vgwm4s6pc2q38hxrma9h4ycgtn4kzaq0we0d4mjtq030fu6zsfqrzjq2gyp9za7vc7vd8m59fvu63pu00u4pak35n4upuv4mhyw5l586dvkf6vkyqq20gqqqqqqqqpqqqqqzsqqc9qyyssq6frgq8gly29thefxg83y57hv564npas5dqc0cslml2q3cj0usrdptqa5p2el3df0c0xc6raxnty06m745l87v4qausuq9xh0wj2x57cpxsxxcz".to_string();
let invoice = "lntb5125180n1pnwmtnvpp5rt2ptzc4329nr8f9qnaeg8nszs2w40rr922wnqe6uw6rt6sfz0escqpjsp5lcvu233r6wmd6tvqvpnwpe97tjwzh0kygtjz9htw2y7j6h5grgkq9q7sqqqqqqqqqqqqqqqqqqqsqqqqqysgqdqqmqz9gxqyjw5qrzjqwfn3p9278ttzzpe0e00uhyxhned3j5d9acqak5emwfpflp8z2cnflctr6qq3f9n3gqqqqlgqqqqqeqqjqmrtu79yvjazp5tcn6nscf27arhevexq64yd0jjmkc8hxlqkh5ywzwk209xvmf484uutvjqv5rtgq0aulm9e4al72wwljm97a3vdcgxcq4vcmxq".to_string();
let refund_address = "tb1qq20a7gqewc0un9mxxlqyqwn7ut7zjrj9y3d0mu".to_string();

let boltz_api_v2 = BoltzApiClientV2::new(BOLTZ_TESTNET_URL_V2);
Expand Down

0 comments on commit e482332

Please sign in to comment.