Skip to content
This repository was archived by the owner on Feb 3, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions node-manager/src/background.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,8 @@ impl BackgroundProcessor {
{
let stop_thread = Arc::new(AtomicBool::new(false));
let stop_thread_clone = stop_thread.clone();
let _ = spawn_local(async move {
let _ = define_run_body!(
spawn_local(async move {
define_run_body!(
persister,
event_handler,
chain_monitor,
Expand Down
21 changes: 21 additions & 0 deletions node-manager/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bdk::esplora_client;
use lightning::ln::peer_handler::PeerHandleError;
use lightning_invoice::payment::PaymentError;
use thiserror::Error;
use wasm_bindgen::JsValue;

Expand Down Expand Up @@ -114,6 +115,16 @@ impl From<PeerHandleError> for MutinyError {
}
}

impl From<PaymentError> for MutinyError {
fn from(e: PaymentError) -> Self {
match e {
PaymentError::Invoice(_) => Self::InvoiceInvalid,
PaymentError::Routing(_) => Self::RoutingFailed,
PaymentError::Sending(_) => Self::RoutingFailed,
}
}
}

impl From<MutinyStorageError> for bdk::Error {
fn from(e: MutinyStorageError) -> Self {
match e {
Expand Down Expand Up @@ -232,6 +243,16 @@ impl From<bitcoin::util::address::Error> for MutinyJsError {
}
}

impl From<PaymentError> for MutinyJsError {
fn from(e: PaymentError) -> Self {
match e {
PaymentError::Invoice(_) => Self::InvoiceInvalid,
PaymentError::Routing(_) => Self::RoutingFailed,
PaymentError::Sending(_) => Self::RoutingFailed,
}
}
}

impl From<esplora_client::Error> for MutinyJsError {
fn from(_e: esplora_client::Error) -> Self {
// This is most likely a chain access failure
Expand Down
18 changes: 11 additions & 7 deletions node-manager/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub(crate) struct PaymentInfo {
pub secret: Option<[u8; 32]>,
pub status: HTLCStatus,
pub amt_msat: MillisatAmount,
pub fee_paid_msat: Option<u64>,
}

#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -97,11 +98,11 @@ impl LdkEventHandler for EventHandler {
.to_address();

let wallet_thread = self.wallet.clone();
let channel_values_satoshis_thread = channel_value_satoshis.clone();
let channel_values_satoshis_thread = *channel_value_satoshis;
let channel_manager_thread = self.channel_manager.clone();
let logger_thread = self.logger.clone();
let temporary_channel_id_thread = temporary_channel_id.clone();
let counterparty_node_id_thread = counterparty_node_id.clone();
let temporary_channel_id_thread = *temporary_channel_id;
let counterparty_node_id_thread = *counterparty_node_id;
spawn_local(async move {
let psbt = match wallet_thread
.create_signed_psbt(addr, channel_values_satoshis_thread, None)
Expand Down Expand Up @@ -239,6 +240,7 @@ impl LdkEventHandler for EventHandler {
secret: payment_secret,
status: HTLCStatus::Succeeded,
amt_msat: MillisatAmount(Some(*amount_msat)),
fee_paid_msat: None,
};
match self
.persister
Expand All @@ -261,6 +263,7 @@ impl LdkEventHandler for EventHandler {
Event::PaymentSent {
payment_preimage,
payment_hash,
fee_paid_msat,
..
} => {
self.logger.log(&Record::new(
Expand All @@ -271,14 +274,15 @@ impl LdkEventHandler for EventHandler {
0,
));

match self.persister.read_payment_info(*payment_hash, true) {
match self.persister.read_payment_info(*payment_hash, false) {
Some(mut saved_payment_info) => {
saved_payment_info.status = HTLCStatus::Succeeded;
saved_payment_info.preimage = Some(payment_preimage.0);
saved_payment_info.fee_paid_msat = *fee_paid_msat;
match self.persister.persist_payment_info(
*payment_hash,
saved_payment_info,
true,
false,
) {
Ok(_) => (),
Err(e) => {
Expand Down Expand Up @@ -359,13 +363,13 @@ impl LdkEventHandler for EventHandler {
0,
));

match self.persister.read_payment_info(*payment_hash, true) {
match self.persister.read_payment_info(*payment_hash, false) {
Some(mut saved_payment_info) => {
saved_payment_info.status = HTLCStatus::Failed;
match self.persister.persist_payment_info(
*payment_hash,
saved_payment_info,
true,
false,
) {
Ok(_) => (),
Err(e) => {
Expand Down
8 changes: 4 additions & 4 deletions node-manager/src/localstorage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ impl MutinyBrowserStorage {
let data = serde_json::to_string(&value)?;
// Only bother encrypting if a password is set
if self.password.is_empty() {
return Ok(LocalStorage::set(key, data)?);
Ok(LocalStorage::set(key, data)?)
} else {
let ciphertext = encrypt(data.as_str(), self.password.as_str());
return Ok(LocalStorage::set(key, ciphertext)?);
Ok(LocalStorage::set(key, ciphertext)?)
}
}

Expand All @@ -49,10 +49,10 @@ impl MutinyBrowserStorage {
let data: String = LocalStorage::get(key)?;
// Only bother decrypting if a password is set
if self.password.is_empty() {
return Ok(serde_json::from_str::<T>(data.as_str())?);
Ok(serde_json::from_str::<T>(data.as_str())?)
} else {
let decrypted_data = decrypt(data.as_str(), self.password.as_str());
return Ok(serde_json::from_str::<T>(decrypted_data.as_str())?);
Ok(serde_json::from_str::<T>(decrypted_data.as_str())?)
}
}

Expand Down
5 changes: 3 additions & 2 deletions node-manager/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ pub struct Node {
pub chain_monitor: Arc<ChainMonitor>,
pub invoice_payer: Arc<InvoicePayer<EventHandler>>,
network: Network,
persister: Arc<MutinyNodePersister>,
pub persister: Arc<MutinyNodePersister>,
_background_processor: BackgroundProcessor,
logger: Arc<MutinyLogger>,
}
Expand Down Expand Up @@ -309,7 +309,7 @@ impl Node {
route_hints: Vec<PhantomRouteHints>,
) -> Result<Invoice, MutinyError> {
let invoice = match create_phantom_invoice::<InMemorySigner, Arc<PhantomKeysManager>>(
Some(amount_sat * 1),
Some(amount_sat * 1_000),
None,
description,
1500,
Expand Down Expand Up @@ -345,6 +345,7 @@ impl Node {
secret: Some(invoice.payment_secret().0),
status: HTLCStatus::Pending,
amt_msat: MillisatAmount(Some(amount_sat * 1000)),
fee_paid_msat: None,
};
match self
.persister
Expand Down
81 changes: 53 additions & 28 deletions node-manager/src/nodemanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ use bdk::wallet::AddressIndex;
use bip39::Mnemonic;
use bitcoin::consensus::deserialize;
use bitcoin::hashes::hex::{FromHex, ToHex};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::{Address, Network, OutPoint, PublicKey, Transaction};
use bitcoin_hashes::Hash;
use futures::lock::Mutex;
use lightning::chain::chaininterface::BroadcasterInterface;
use lightning::chain::keysinterface::{KeysInterface, Recipient};
use lightning::chain::Confirm;
use lightning::ln::channelmanager::{ChannelDetails, PhantomRouteHints};
use lightning::ln::PaymentPreimage;
use lightning::ln::{PaymentHash, PaymentPreimage};
use lightning_invoice::{Invoice, InvoiceDescription};
use log::{debug, error, info};
use secp256k1::ThirtyTwoByteHash;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use wasm_bindgen::prelude::*;
Expand Down Expand Up @@ -78,7 +81,7 @@ pub struct MutinyInvoice {
payment_hash: String,
preimage: Option<String>,
pub amount_sats: Option<u64>,
pub expire: Option<u64>,
pub expire: u64,
pub paid: bool,
pub fees_paid: Option<u64>,
pub is_send: bool,
Expand Down Expand Up @@ -114,13 +117,16 @@ impl From<Invoice> for MutinyInvoice {
InvoiceDescription::Hash(_) => None,
};

let timestamp = value.duration_since_epoch().as_secs();
let expiry = timestamp + value.expiry_time().as_secs();

MutinyInvoice {
bolt11: value.to_string(),
description,
payment_hash: value.payment_hash().to_owned().to_hex(),
preimage: None,
amount_sats: value.amount_milli_satoshis().map(|m| m / 1000),
expire: None, // todo
expire: expiry,
paid: false,
fees_paid: None, // todo
is_send: false, // todo this could be bad
Expand Down Expand Up @@ -245,7 +251,7 @@ impl NodeManager {
let id = node
.keys_manager
.get_node_id(Recipient::Node)
.expect(format!("Failed to get node id for {}", node_item.0).as_str());
.expect("Failed to get node id");

nodes_map.insert(id.to_hex(), Arc::new(node));
}
Expand Down Expand Up @@ -502,33 +508,45 @@ impl NodeManager {
}

#[wasm_bindgen]
// todo change return to MutinyInvoice
pub async fn pay_invoice(
&self,
from_node: String,
invoice_str: String,
) -> Result<(), MutinyJsError> {
) -> Result<MutinyInvoice, MutinyJsError> {
let nodes = self.nodes.lock().await;
let node = nodes.get(from_node.as_str()).unwrap();

let invoice =
Invoice::from_str(invoice_str.as_str()).map_err(|_| MutinyJsError::InvoiceInvalid)?;
let pay_result = node.invoice_payer.pay_invoice(&invoice);

match pay_result {
Ok(_) => Ok(info!("Payment successful!")),
Err(_) => Err(MutinyJsError::RoutingFailed),
// todo ensure payment hash is unique
let payment_hash = PaymentHash(invoice.payment_hash().into_32());

let _ = node.invoice_payer.pay_invoice(&invoice)?;

info!("Payment successful!");

match node.persister.read_payment_info(payment_hash, false) {
Some(payment_info) => {
let mut mutiny_invoice = MutinyInvoice::from(invoice);

mutiny_invoice.is_send = true;
mutiny_invoice.paid = true;
mutiny_invoice.fees_paid = payment_info.fee_paid_msat;

Ok(mutiny_invoice)
}
None => Err(MutinyJsError::ReadError),
}
}

#[wasm_bindgen]
// todo change return to MutinyInvoice
pub async fn keysend(
&self,
from_node: String,
to_node: String,
amt_sats: u64,
) -> Result<(), MutinyJsError> {
) -> Result<MutinyInvoice, MutinyJsError> {
let nodes = self.nodes.lock().await;
debug!("Keysending to {to_node}");
let node = nodes.get(from_node.as_str()).unwrap();
Expand All @@ -542,27 +560,34 @@ impl NodeManager {
getrandom::getrandom(&mut entropy).map_err(|_| MutinyError::SeedGenerationFailed)?;
let preimage = PaymentPreimage(entropy);

let amt_msats = amt_sats * 1000;
let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());

let chans = node.channel_manager.list_channels();
let chan = chans.first().unwrap();
let usable = node.channel_manager.list_usable_channels();

info!(
"confirmations_required: {}",
chan.confirmations_required.unwrap_or(69)
);
info!("is_channel_ready: {}", chan.is_channel_ready);
info!("is_usable: {}", chan.is_usable);
info!("is_public: {}", chan.is_public);
info!("usable: {}", usable.len());
let amt_msats = amt_sats * 1000;

let _ = node
.invoice_payer
.pay_pubkey(node_id, preimage, amt_msats, 40)
.unwrap();
.pay_pubkey(node_id, preimage, amt_msats, 40)?;

info!("Keysend to {to_node} successful!");

match node.persister.read_payment_info(payment_hash, false) {
Some(payment_info) => {
let mutiny_invoice = MutinyInvoice {
bolt11: "".to_string(),
description: None,
payment_hash: payment_hash.0.to_hex(),
preimage: Some(preimage.0.to_hex()),
amount_sats: Some(amt_sats),
expire: 0,
paid: true,
fees_paid: payment_info.fee_paid_msat,
is_send: true,
};

Ok(info!("Keysend successful!"))
Ok(mutiny_invoice)
}
None => Err(MutinyJsError::ReadError),
}
}

#[wasm_bindgen]
Expand Down