use std::future::Future;
use std::ops::Deref;
use std::pin::Pin;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use bdk_chain::spk_client::{FullScanRequest, SyncRequest};
use bdk_wallet::descriptor::ExtendedDescriptor;
#[allow(deprecated)]
use bdk_wallet::SignOptions;
use bdk_wallet::{Balance, KeychainKind, PersistedWallet, Update};
use bitcoin::address::NetworkUnchecked;
use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::hashes::Hash;
use bitcoin::key::XOnlyPublicKey;
use bitcoin::psbt::{self, Psbt};
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::{All, PublicKey, Scalar, Secp256k1, SecretKey};
use bitcoin::{
Address, Amount, FeeRate, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, Weight,
WitnessProgram, WitnessVersion,
};
use lightning::chain::chaininterface::BroadcasterInterface;
use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
use lightning::chain::{BestBlock, Listen};
use lightning::events::bump_transaction::{Input, Utxo, WalletSource};
use lightning::ln::channelmanager::PaymentId;
use lightning::ln::funding::FundingTxInput;
use lightning::ln::inbound_payment::ExpandedKey;
use lightning::ln::msgs::UnsignedGossipMessage;
use lightning::ln::script::ShutdownScript;
use lightning::sign::{
ChangeDestinationSource, EntropySource, InMemorySigner, KeysManager, NodeSigner, OutputSpender,
PeerStorageKey, Recipient, SignerProvider, SpendableOutputDescriptor,
};
use lightning::util::message_signing;
use lightning_invoice::RawBolt11Invoice;
use persist::KVStoreWalletPersister;
use crate::config::Config;
use crate::fee_estimator::{ConfirmationTarget, FeeEstimator, OnchainFeeEstimator};
use crate::logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
use crate::payment::store::ConfirmationStatus;
use crate::payment::{PaymentDetails, PaymentDirection, PaymentStatus};
use crate::types::{Broadcaster, PaymentStore};
use crate::Error;
pub(crate) enum OnchainSendAmount {
ExactRetainingReserve { amount_sats: u64, cur_anchor_reserve_sats: u64 },
AllRetainingReserve { cur_anchor_reserve_sats: u64 },
AllDrainingReserve,
}
pub(crate) mod persist;
pub(crate) mod ser;
pub(crate) struct Wallet {
inner: Mutex<PersistedWallet<KVStoreWalletPersister>>,
persister: Mutex<KVStoreWalletPersister>,
broadcaster: Arc<Broadcaster>,
fee_estimator: Arc<OnchainFeeEstimator>,
payment_store: Arc<PaymentStore>,
config: Arc<Config>,
logger: Arc<Logger>,
}
impl Wallet {
pub(crate) fn new(
wallet: bdk_wallet::PersistedWallet<KVStoreWalletPersister>,
wallet_persister: KVStoreWalletPersister, broadcaster: Arc<Broadcaster>,
fee_estimator: Arc<OnchainFeeEstimator>, payment_store: Arc<PaymentStore>,
config: Arc<Config>, logger: Arc<Logger>,
) -> Self {
let inner = Mutex::new(wallet);
let persister = Mutex::new(wallet_persister);
Self { inner, persister, broadcaster, fee_estimator, payment_store, config, logger }
}
pub(crate) fn get_full_scan_request(&self) -> FullScanRequest<KeychainKind> {
self.inner.lock().unwrap().start_full_scan().build()
}
pub(crate) fn get_incremental_sync_request(&self) -> SyncRequest<(KeychainKind, u32)> {
self.inner.lock().unwrap().start_sync_with_revealed_spks().build()
}
pub(crate) fn get_cached_txs(&self) -> Vec<Arc<Transaction>> {
self.inner.lock().unwrap().tx_graph().full_txs().map(|tx_node| tx_node.tx).collect()
}
pub(crate) fn get_unconfirmed_txids(&self) -> Vec<Txid> {
self.inner
.lock()
.unwrap()
.transactions()
.filter(|t| t.chain_position.is_unconfirmed())
.map(|t| t.tx_node.txid)
.collect()
}
pub(crate) fn current_best_block(&self) -> BestBlock {
let checkpoint = self.inner.lock().unwrap().latest_checkpoint();
BestBlock { block_hash: checkpoint.hash(), height: checkpoint.height() }
}
pub(crate) fn apply_update(&self, update: impl Into<Update>) -> Result<(), Error> {
let mut locked_wallet = self.inner.lock().unwrap();
match locked_wallet.apply_update(update) {
Ok(()) => {
let mut locked_persister = self.persister.lock().unwrap();
locked_wallet.persist(&mut locked_persister).map_err(|e| {
log_error!(self.logger, "Failed to persist wallet: {}", e);
Error::PersistenceFailed
})?;
self.update_payment_store(&mut *locked_wallet).map_err(|e| {
log_error!(self.logger, "Failed to update payment store: {}", e);
Error::PersistenceFailed
})?;
Ok(())
},
Err(e) => {
log_error!(self.logger, "Sync failed due to chain connection error: {}", e);
Err(Error::WalletOperationFailed)
},
}
}
pub(crate) fn apply_mempool_txs(
&self, unconfirmed_txs: Vec<(Transaction, u64)>, evicted_txids: Vec<(Txid, u64)>,
) -> Result<(), Error> {
let mut locked_wallet = self.inner.lock().unwrap();
locked_wallet.apply_unconfirmed_txs(unconfirmed_txs);
locked_wallet.apply_evicted_txs(evicted_txids);
let mut locked_persister = self.persister.lock().unwrap();
locked_wallet.persist(&mut locked_persister).map_err(|e| {
log_error!(self.logger, "Failed to persist wallet: {}", e);
Error::PersistenceFailed
})?;
Ok(())
}
fn update_payment_store<'a>(
&self, locked_wallet: &'a mut PersistedWallet<KVStoreWalletPersister>,
) -> Result<(), Error> {
for wtx in locked_wallet.transactions() {
let id = PaymentId(wtx.tx_node.txid.to_byte_array());
let txid = wtx.tx_node.txid;
let (payment_status, confirmation_status) = match wtx.chain_position {
bdk_chain::ChainPosition::Confirmed { anchor, .. } => {
let confirmation_height = anchor.block_id.height;
let cur_height = locked_wallet.latest_checkpoint().height();
let payment_status = if cur_height >= confirmation_height + ANTI_REORG_DELAY - 1
{
PaymentStatus::Succeeded
} else {
PaymentStatus::Pending
};
let confirmation_status = ConfirmationStatus::Confirmed {
block_hash: anchor.block_id.hash,
height: confirmation_height,
timestamp: anchor.confirmation_time,
};
(payment_status, confirmation_status)
},
bdk_chain::ChainPosition::Unconfirmed { .. } => {
(PaymentStatus::Pending, ConfirmationStatus::Unconfirmed)
},
};
let kind = crate::payment::PaymentKind::Onchain { txid, status: confirmation_status };
let fee = locked_wallet.calculate_fee(&wtx.tx_node.tx).unwrap_or(Amount::ZERO);
let (sent, received) = locked_wallet.sent_and_received(&wtx.tx_node.tx);
let (direction, amount_msat) = if sent > received {
let direction = PaymentDirection::Outbound;
let amount_msat = Some(
sent.to_sat().saturating_sub(fee.to_sat()).saturating_sub(received.to_sat())
* 1000,
);
(direction, amount_msat)
} else {
let direction = PaymentDirection::Inbound;
let amount_msat = Some(
received.to_sat().saturating_sub(sent.to_sat().saturating_sub(fee.to_sat()))
* 1000,
);
(direction, amount_msat)
};
let fee_paid_msat = Some(fee.to_sat() * 1000);
let payment = PaymentDetails::new(
id,
kind,
amount_msat,
fee_paid_msat,
direction,
payment_status,
);
self.payment_store.insert_or_update(payment)?;
}
Ok(())
}
#[allow(deprecated)]
pub(crate) fn create_funding_transaction(
&self, output_script: ScriptBuf, amount: Amount, confirmation_target: ConfirmationTarget,
locktime: LockTime,
) -> Result<Transaction, Error> {
let fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target);
let mut locked_wallet = self.inner.lock().unwrap();
let mut tx_builder = locked_wallet.build_tx();
tx_builder.add_recipient(output_script, amount).fee_rate(fee_rate).nlocktime(locktime);
let mut psbt = match tx_builder.finish() {
Ok(psbt) => {
log_trace!(self.logger, "Created funding PSBT: {:?}", psbt);
psbt
},
Err(err) => {
log_error!(self.logger, "Failed to create funding transaction: {}", err);
return Err(err.into());
},
};
match locked_wallet.sign(&mut psbt, SignOptions::default()) {
Ok(finalized) => {
if !finalized {
return Err(Error::OnchainTxCreationFailed);
}
},
Err(err) => {
log_error!(self.logger, "Failed to create funding transaction: {}", err);
return Err(err.into());
},
}
let mut locked_persister = self.persister.lock().unwrap();
locked_wallet.persist(&mut locked_persister).map_err(|e| {
log_error!(self.logger, "Failed to persist wallet: {}", e);
Error::PersistenceFailed
})?;
let tx = psbt.extract_tx().map_err(|e| {
log_error!(self.logger, "Failed to extract transaction: {}", e);
e
})?;
Ok(tx)
}
pub(crate) fn get_new_address(&self) -> Result<bitcoin::Address, Error> {
let mut locked_wallet = self.inner.lock().unwrap();
let mut locked_persister = self.persister.lock().unwrap();
let address_info = locked_wallet.reveal_next_address(KeychainKind::External);
locked_wallet.persist(&mut locked_persister).map_err(|e| {
log_error!(self.logger, "Failed to persist wallet: {}", e);
Error::PersistenceFailed
})?;
Ok(address_info.address)
}
pub(crate) fn get_new_internal_address(&self) -> Result<bitcoin::Address, Error> {
let mut locked_wallet = self.inner.lock().unwrap();
let mut locked_persister = self.persister.lock().unwrap();
let address_info = locked_wallet.next_unused_address(KeychainKind::Internal);
locked_wallet.persist(&mut locked_persister).map_err(|e| {
log_error!(self.logger, "Failed to persist wallet: {}", e);
Error::PersistenceFailed
})?;
Ok(address_info.address)
}
pub(crate) fn cancel_tx(&self, tx: &Transaction) -> Result<(), Error> {
let mut locked_wallet = self.inner.lock().unwrap();
let mut locked_persister = self.persister.lock().unwrap();
locked_wallet.cancel_tx(tx);
locked_wallet.persist(&mut locked_persister).map_err(|e| {
log_error!(self.logger, "Failed to persist wallet: {}", e);
Error::PersistenceFailed
})?;
Ok(())
}
pub(crate) fn get_balances(
&self, total_anchor_channels_reserve_sats: u64,
) -> Result<(u64, u64), Error> {
let balance = self.inner.lock().unwrap().balance();
#[cfg(debug_assertions)]
if balance.confirmed != Amount::ZERO {
debug_assert!(
self.list_confirmed_utxos_inner().map_or(false, |v| !v.is_empty()),
"Confirmed amounts should always be available for Anchor spending"
);
}
self.get_balances_inner(balance, total_anchor_channels_reserve_sats)
}
fn get_balances_inner(
&self, balance: Balance, total_anchor_channels_reserve_sats: u64,
) -> Result<(u64, u64), Error> {
let (total, spendable) = (
balance.total().to_sat(),
balance.trusted_spendable().to_sat().saturating_sub(total_anchor_channels_reserve_sats),
);
Ok((total, spendable))
}
pub(crate) fn get_spendable_amount_sats(
&self, total_anchor_channels_reserve_sats: u64,
) -> Result<u64, Error> {
self.get_balances(total_anchor_channels_reserve_sats).map(|(_, s)| s)
}
pub(crate) fn parse_and_validate_address(&self, address: &Address) -> Result<Address, Error> {
Address::<NetworkUnchecked>::from_str(address.to_string().as_str())
.map_err(|_| Error::InvalidAddress)?
.require_network(self.config.network)
.map_err(|_| Error::InvalidAddress)
}
#[allow(deprecated)]
pub(crate) fn send_to_address(
&self, address: &bitcoin::Address, send_amount: OnchainSendAmount,
fee_rate: Option<FeeRate>,
) -> Result<Txid, Error> {
self.parse_and_validate_address(&address)?;
let confirmation_target = ConfirmationTarget::OnchainPayment;
let fee_rate =
fee_rate.unwrap_or_else(|| self.fee_estimator.estimate_fee_rate(confirmation_target));
let tx = {
let mut locked_wallet = self.inner.lock().unwrap();
const DUST_LIMIT_SATS: u64 = 546;
let tx_builder = match send_amount {
OnchainSendAmount::ExactRetainingReserve { amount_sats, .. } => {
let mut tx_builder = locked_wallet.build_tx();
let amount = Amount::from_sat(amount_sats);
tx_builder.add_recipient(address.script_pubkey(), amount).fee_rate(fee_rate);
tx_builder
},
OnchainSendAmount::AllRetainingReserve { cur_anchor_reserve_sats }
if cur_anchor_reserve_sats > DUST_LIMIT_SATS =>
{
let change_address_info = locked_wallet.peek_address(KeychainKind::Internal, 0);
let balance = locked_wallet.balance();
let spendable_amount_sats = self
.get_balances_inner(balance, cur_anchor_reserve_sats)
.map(|(_, s)| s)
.unwrap_or(0);
let tmp_tx = {
let mut tmp_tx_builder = locked_wallet.build_tx();
tmp_tx_builder
.drain_wallet()
.drain_to(address.script_pubkey())
.add_recipient(
change_address_info.address.script_pubkey(),
Amount::from_sat(cur_anchor_reserve_sats),
)
.fee_rate(fee_rate);
match tmp_tx_builder.finish() {
Ok(psbt) => psbt.unsigned_tx,
Err(err) => {
log_error!(
self.logger,
"Failed to create temporary transaction: {}",
err
);
return Err(err.into());
},
}
};
let estimated_tx_fee = locked_wallet.calculate_fee(&tmp_tx).map_err(|e| {
log_error!(
self.logger,
"Failed to calculate fee of temporary transaction: {}",
e
);
e
})?;
locked_wallet.cancel_tx(&tmp_tx);
let estimated_spendable_amount = Amount::from_sat(
spendable_amount_sats.saturating_sub(estimated_tx_fee.to_sat()),
);
if estimated_spendable_amount == Amount::ZERO {
log_error!(self.logger,
"Unable to send payment without infringing on Anchor reserves. Available: {}sats, estimated fee required: {}sats.",
spendable_amount_sats,
estimated_tx_fee,
);
return Err(Error::InsufficientFunds);
}
let mut tx_builder = locked_wallet.build_tx();
tx_builder
.add_recipient(address.script_pubkey(), estimated_spendable_amount)
.fee_absolute(estimated_tx_fee);
tx_builder
},
OnchainSendAmount::AllDrainingReserve
| OnchainSendAmount::AllRetainingReserve { cur_anchor_reserve_sats: _ } => {
let mut tx_builder = locked_wallet.build_tx();
tx_builder.drain_wallet().drain_to(address.script_pubkey()).fee_rate(fee_rate);
tx_builder
},
};
let mut psbt = match tx_builder.finish() {
Ok(psbt) => {
log_trace!(self.logger, "Created PSBT: {:?}", psbt);
psbt
},
Err(err) => {
log_error!(self.logger, "Failed to create transaction: {}", err);
return Err(err.into());
},
};
match send_amount {
OnchainSendAmount::ExactRetainingReserve {
amount_sats,
cur_anchor_reserve_sats,
} => {
let balance = locked_wallet.balance();
let spendable_amount_sats = self
.get_balances_inner(balance, cur_anchor_reserve_sats)
.map(|(_, s)| s)
.unwrap_or(0);
let tx_fee_sats = locked_wallet
.calculate_fee(&psbt.unsigned_tx)
.map_err(|e| {
log_error!(
self.logger,
"Failed to calculate fee of candidate transaction: {}",
e
);
e
})?
.to_sat();
if spendable_amount_sats < amount_sats.saturating_add(tx_fee_sats) {
log_error!(self.logger,
"Unable to send payment due to insufficient funds. Available: {}sats, Required: {}sats + {}sats fee",
spendable_amount_sats,
amount_sats,
tx_fee_sats,
);
return Err(Error::InsufficientFunds);
}
},
OnchainSendAmount::AllRetainingReserve { cur_anchor_reserve_sats } => {
let balance = locked_wallet.balance();
let spendable_amount_sats = self
.get_balances_inner(balance, cur_anchor_reserve_sats)
.map(|(_, s)| s)
.unwrap_or(0);
let (sent, received) = locked_wallet.sent_and_received(&psbt.unsigned_tx);
let drain_amount = sent - received;
if spendable_amount_sats < drain_amount.to_sat() {
log_error!(self.logger,
"Unable to send payment due to insufficient funds. Available: {}sats, Required: {}",
spendable_amount_sats,
drain_amount,
);
return Err(Error::InsufficientFunds);
}
},
_ => {},
}
match locked_wallet.sign(&mut psbt, SignOptions::default()) {
Ok(finalized) => {
if !finalized {
return Err(Error::OnchainTxCreationFailed);
}
},
Err(err) => {
log_error!(self.logger, "Failed to create transaction: {}", err);
return Err(err.into());
},
}
let mut locked_persister = self.persister.lock().unwrap();
locked_wallet.persist(&mut locked_persister).map_err(|e| {
log_error!(self.logger, "Failed to persist wallet: {}", e);
Error::PersistenceFailed
})?;
psbt.extract_tx().map_err(|e| {
log_error!(self.logger, "Failed to extract transaction: {}", e);
e
})?
};
self.broadcaster.broadcast_transactions(&[&tx]);
let txid = tx.compute_txid();
match send_amount {
OnchainSendAmount::ExactRetainingReserve { amount_sats, .. } => {
log_info!(
self.logger,
"Created new transaction {} sending {}sats on-chain to address {}",
txid,
amount_sats,
address
);
},
OnchainSendAmount::AllRetainingReserve { cur_anchor_reserve_sats } => {
log_info!(
self.logger,
"Created new transaction {} sending available on-chain funds retaining a reserve of {}sats to address {}",
txid,
cur_anchor_reserve_sats,
address,
);
},
OnchainSendAmount::AllDrainingReserve => {
log_info!(
self.logger,
"Created new transaction {} sending all available on-chain funds to address {}",
txid,
address
);
},
}
Ok(txid)
}
pub(crate) fn select_confirmed_utxos(
&self, must_spend: Vec<Input>, must_pay_to: &[TxOut], fee_rate: FeeRate,
) -> Result<Vec<FundingTxInput>, ()> {
let mut locked_wallet = self.inner.lock().unwrap();
debug_assert!(matches!(
locked_wallet.public_descriptor(KeychainKind::External),
ExtendedDescriptor::Wpkh(_)
));
debug_assert!(matches!(
locked_wallet.public_descriptor(KeychainKind::Internal),
ExtendedDescriptor::Wpkh(_)
));
let mut tx_builder = locked_wallet.build_tx();
tx_builder.only_witness_utxo();
for input in &must_spend {
let psbt_input = psbt::Input {
witness_utxo: Some(input.previous_utxo.clone()),
..Default::default()
};
let weight = Weight::from_wu(input.satisfaction_weight);
tx_builder.add_foreign_utxo(input.outpoint, psbt_input, weight).map_err(|_| ())?;
}
for output in must_pay_to {
tx_builder.add_recipient(output.script_pubkey.clone(), output.value);
}
tx_builder.fee_rate(fee_rate);
tx_builder.exclude_unconfirmed();
tx_builder
.finish()
.map_err(|e| {
log_error!(self.logger, "Failed to select confirmed UTXOs: {}", e);
})?
.unsigned_tx
.input
.iter()
.filter(|txin| must_spend.iter().all(|input| input.outpoint != txin.previous_output))
.filter_map(|txin| {
locked_wallet
.tx_details(txin.previous_output.txid)
.map(|tx_details| tx_details.tx.deref().clone())
.map(|prevtx| FundingTxInput::new_p2wpkh(prevtx, txin.previous_output.vout))
})
.collect::<Result<Vec<_>, ()>>()
}
fn list_confirmed_utxos_inner(&self) -> Result<Vec<Utxo>, ()> {
let locked_wallet = self.inner.lock().unwrap();
let mut utxos = Vec::new();
let confirmed_txs: Vec<Txid> = locked_wallet
.transactions()
.filter(|t| t.chain_position.is_confirmed())
.map(|t| t.tx_node.txid)
.collect();
let unspent_confirmed_utxos =
locked_wallet.list_unspent().filter(|u| confirmed_txs.contains(&u.outpoint.txid));
for u in unspent_confirmed_utxos {
let script_pubkey = u.txout.script_pubkey;
match script_pubkey.witness_version() {
Some(version @ WitnessVersion::V0) => {
let witness_bytes = &script_pubkey.as_bytes()[2..];
let witness_program =
WitnessProgram::new(version, witness_bytes).map_err(|e| {
log_error!(self.logger, "Failed to retrieve script payload: {}", e);
})?;
let wpkh = WPubkeyHash::from_slice(&witness_program.program().as_bytes())
.map_err(|e| {
log_error!(self.logger, "Failed to retrieve script payload: {}", e);
})?;
let utxo = Utxo::new_v0_p2wpkh(u.outpoint, u.txout.value, &wpkh);
utxos.push(utxo);
},
Some(version @ WitnessVersion::V1) => {
let witness_bytes = &script_pubkey.as_bytes()[2..];
let witness_program =
WitnessProgram::new(version, witness_bytes).map_err(|e| {
log_error!(self.logger, "Failed to retrieve script payload: {}", e);
})?;
XOnlyPublicKey::from_slice(&witness_program.program().as_bytes()).map_err(
|e| {
log_error!(self.logger, "Failed to retrieve script payload: {}", e);
},
)?;
let utxo = Utxo {
outpoint: u.outpoint,
output: TxOut {
value: u.txout.value,
script_pubkey: ScriptBuf::new_witness_program(&witness_program),
},
satisfaction_weight: 1 * WITNESS_SCALE_FACTOR as u64 +
1 + 1 + 64, };
utxos.push(utxo);
},
Some(version) => {
log_error!(self.logger, "Unexpected witness version: {}", version,);
},
None => {
log_error!(
self.logger,
"Tried to use a non-witness script. This must never happen."
);
panic!("Tried to use a non-witness script. This must never happen.");
},
}
}
Ok(utxos)
}
#[allow(deprecated)]
fn get_change_script_inner(&self) -> Result<ScriptBuf, ()> {
let mut locked_wallet = self.inner.lock().unwrap();
let mut locked_persister = self.persister.lock().unwrap();
let address_info = locked_wallet.next_unused_address(KeychainKind::Internal);
locked_wallet.persist(&mut locked_persister).map_err(|e| {
log_error!(self.logger, "Failed to persist wallet: {}", e);
()
})?;
Ok(address_info.address.script_pubkey())
}
#[allow(deprecated)]
pub(crate) fn sign_owned_inputs(&self, unsigned_tx: Transaction) -> Result<Transaction, ()> {
let locked_wallet = self.inner.lock().unwrap();
let mut psbt = Psbt::from_unsigned_tx(unsigned_tx).map_err(|e| {
log_error!(self.logger, "Failed to construct PSBT: {}", e);
})?;
for (i, txin) in psbt.unsigned_tx.input.iter().enumerate() {
if let Some(utxo) = locked_wallet.get_utxo(txin.previous_output) {
debug_assert!(!utxo.is_spent);
psbt.inputs[i] = locked_wallet.get_psbt_input(utxo, None, true).map_err(|e| {
log_error!(self.logger, "Failed to construct PSBT input: {}", e);
})?;
}
}
let mut sign_options = SignOptions::default();
sign_options.trust_witness_utxo = true;
match locked_wallet.sign(&mut psbt, sign_options) {
Ok(finalized) => debug_assert!(!finalized),
Err(e) => {
log_error!(self.logger, "Failed to sign owned inputs: {}", e);
return Err(());
},
}
match psbt.extract_tx() {
Ok(tx) => Ok(tx),
Err(bitcoin::psbt::ExtractTxError::MissingInputValue { tx }) => Ok(tx),
Err(e) => {
log_error!(self.logger, "Failed to extract transaction: {}", e);
Err(())
},
}
}
#[allow(deprecated)]
fn sign_psbt_inner(&self, mut psbt: Psbt) -> Result<Transaction, ()> {
let locked_wallet = self.inner.lock().unwrap();
let mut sign_options = SignOptions::default();
sign_options.trust_witness_utxo = true;
match locked_wallet.sign(&mut psbt, sign_options) {
Ok(_finalized) => {
},
Err(err) => {
log_error!(self.logger, "Failed to sign transaction: {}", err);
return Err(());
},
}
let tx = psbt.extract_tx().map_err(|e| {
log_error!(self.logger, "Failed to extract transaction: {}", e);
()
})?;
Ok(tx)
}
}
impl Listen for Wallet {
fn filtered_block_connected(
&self, _header: &bitcoin::block::Header,
_txdata: &lightning::chain::transaction::TransactionData, _height: u32,
) {
debug_assert!(false, "Syncing filtered blocks is currently not supported");
}
fn block_connected(&self, block: &bitcoin::Block, height: u32) {
let mut locked_wallet = self.inner.lock().unwrap();
let pre_checkpoint = locked_wallet.latest_checkpoint();
if pre_checkpoint.height() != height - 1
|| pre_checkpoint.hash() != block.header.prev_blockhash
{
log_debug!(
self.logger,
"Detected reorg while applying a connected block to on-chain wallet: new block with hash {} at height {}",
block.header.block_hash(),
height
);
}
match locked_wallet.apply_block(block, height) {
Ok(()) => {
if let Err(e) = self.update_payment_store(&mut *locked_wallet) {
log_error!(self.logger, "Failed to update payment store: {}", e);
return;
}
},
Err(e) => {
log_error!(
self.logger,
"Failed to apply connected block to on-chain wallet: {}",
e
);
return;
},
};
let mut locked_persister = self.persister.lock().unwrap();
match locked_wallet.persist(&mut locked_persister) {
Ok(_) => (),
Err(e) => {
log_error!(self.logger, "Failed to persist on-chain wallet: {}", e);
return;
},
};
}
fn blocks_disconnected(&self, _fork_point_block: BestBlock) {
}
}
impl WalletSource for Wallet {
fn list_confirmed_utxos<'a>(
&'a self,
) -> Pin<Box<dyn Future<Output = Result<Vec<Utxo>, ()>> + Send + 'a>> {
Box::pin(async move { self.list_confirmed_utxos_inner() })
}
fn get_change_script<'a>(
&'a self,
) -> Pin<Box<dyn Future<Output = Result<ScriptBuf, ()>> + Send + 'a>> {
Box::pin(async move { self.get_change_script_inner() })
}
fn sign_psbt<'a>(
&'a self, psbt: Psbt,
) -> Pin<Box<dyn Future<Output = Result<Transaction, ()>> + Send + 'a>> {
Box::pin(async move { self.sign_psbt_inner(psbt) })
}
}
pub(crate) struct WalletKeysManager {
inner: KeysManager,
wallet: Arc<Wallet>,
logger: Arc<Logger>,
}
impl WalletKeysManager {
pub fn new(
seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, wallet: Arc<Wallet>,
logger: Arc<Logger>,
) -> Self {
let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos, true);
Self { inner, wallet, logger }
}
pub fn sign_message(&self, msg: &[u8]) -> String {
message_signing::sign(msg, &self.inner.get_node_secret_key())
}
pub fn get_node_secret_key(&self) -> SecretKey {
self.inner.get_node_secret_key()
}
pub fn verify_signature(&self, msg: &[u8], sig: &str, pkey: &PublicKey) -> bool {
message_signing::verify(msg, sig, pkey)
}
}
impl NodeSigner for WalletKeysManager {
fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> {
self.inner.get_node_id(recipient)
}
fn ecdh(
&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>,
) -> Result<SharedSecret, ()> {
self.inner.ecdh(recipient, other_key, tweak)
}
fn get_expanded_key(&self) -> ExpandedKey {
self.inner.get_expanded_key()
}
fn get_peer_storage_key(&self) -> PeerStorageKey {
self.inner.get_peer_storage_key()
}
fn get_receive_auth_key(&self) -> lightning::sign::ReceiveAuthKey {
self.inner.get_receive_auth_key()
}
fn sign_invoice(
&self, invoice: &RawBolt11Invoice, recipient: Recipient,
) -> Result<RecoverableSignature, ()> {
self.inner.sign_invoice(invoice, recipient)
}
fn sign_gossip_message(&self, msg: UnsignedGossipMessage<'_>) -> Result<Signature, ()> {
self.inner.sign_gossip_message(msg)
}
fn sign_bolt12_invoice(
&self, invoice: &lightning::offers::invoice::UnsignedBolt12Invoice,
) -> Result<bitcoin::secp256k1::schnorr::Signature, ()> {
self.inner.sign_bolt12_invoice(invoice)
}
fn sign_message(&self, msg: &[u8]) -> Result<String, ()> {
self.inner.sign_message(msg)
}
}
impl OutputSpender for WalletKeysManager {
fn spend_spendable_outputs(
&self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>,
change_destination_script: ScriptBuf, feerate_sat_per_1000_weight: u32,
locktime: Option<LockTime>, secp_ctx: &Secp256k1<All>,
) -> Result<Transaction, ()> {
self.inner.spend_spendable_outputs(
descriptors,
outputs,
change_destination_script,
feerate_sat_per_1000_weight,
locktime,
secp_ctx,
)
}
}
impl EntropySource for WalletKeysManager {
fn get_secure_random_bytes(&self) -> [u8; 32] {
self.inner.get_secure_random_bytes()
}
}
impl SignerProvider for WalletKeysManager {
type EcdsaSigner = InMemorySigner;
fn generate_channel_keys_id(&self, inbound: bool, user_channel_id: u128) -> [u8; 32] {
self.inner.generate_channel_keys_id(inbound, user_channel_id)
}
fn derive_channel_signer(&self, channel_keys_id: [u8; 32]) -> Self::EcdsaSigner {
self.inner.derive_channel_signer(channel_keys_id)
}
fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result<ScriptBuf, ()> {
let address = self.wallet.get_new_address().map_err(|e| {
log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e);
})?;
Ok(address.script_pubkey())
}
fn get_shutdown_scriptpubkey(&self) -> Result<ShutdownScript, ()> {
let address = self.wallet.get_new_address().map_err(|e| {
log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e);
})?;
match address.witness_program() {
Some(program) => ShutdownScript::new_witness_program(&program).map_err(|e| {
log_error!(self.logger, "Invalid shutdown script: {:?}", e);
}),
_ => {
log_error!(
self.logger,
"Tried to use a non-witness address. This must never happen."
);
panic!("Tried to use a non-witness address. This must never happen.");
},
}
}
}
impl ChangeDestinationSource for WalletKeysManager {
fn get_change_destination_script<'a>(
&'a self,
) -> Pin<Box<dyn Future<Output = Result<ScriptBuf, ()>> + Send + 'a>> {
let wallet = Arc::clone(&self.wallet);
let logger = Arc::clone(&self.logger);
Box::pin(async move {
wallet
.get_new_internal_address()
.map_err(|e| {
log_error!(logger, "Failed to retrieve new address from wallet: {}", e);
})
.map(|addr| addr.script_pubkey())
.map_err(|_| ())
})
}
}