Skip to content

Commit

Permalink
WIP: Impl Anchors
Browse files Browse the repository at this point in the history
TODO:
- Decide how to expose enforcing/non-enforcing anchors
- check reserve for enforcing variant
  • Loading branch information
tnull committed Feb 19, 2024
1 parent 395728c commit 210ea82
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 17 deletions.
10 changes: 5 additions & 5 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,12 +630,12 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
// Initialize the ChannelManager
let mut user_config = UserConfig::default();
user_config.channel_handshake_limits.force_announced_channel_preference = false;
user_config.manually_accept_inbound_channels = true;
// Note the channel_handshake_config will be overwritten in `connect_open_channel`, but we
// still set a default here.
user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx =
config.anchor_channels_config.is_some();

if !config.trusted_peers_0conf.is_empty() {
// Manually accept inbound channels if we expect 0conf channel requests, avoid
// generating the events otherwise.
user_config.manually_accept_inbound_channels = true;
}
let channel_manager = {
if let Ok(res) = kv_store.read(
CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE,
Expand Down
49 changes: 43 additions & 6 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::types::{Sweeper, Wallet};
use crate::{
hex_utils, ChannelManager, Config, Error, NetworkGraph, PeerInfo, PeerStore, UserChannelId,
hex_utils, BumpTransactionEventHandler, ChannelManager, Config, Error, NetworkGraph, PeerInfo,
PeerStore, UserChannelId,
};

use crate::payment_store::{
Expand All @@ -11,9 +12,10 @@ use crate::io::{
EVENT_QUEUE_PERSISTENCE_KEY, EVENT_QUEUE_PERSISTENCE_PRIMARY_NAMESPACE,
EVENT_QUEUE_PERSISTENCE_SECONDARY_NAMESPACE,
};
use crate::logger::{log_error, log_info, Logger};
use crate::logger::{log_debug, log_error, log_info, Logger};

use lightning::chain::chaininterface::ConfirmationTarget;
use lightning::events::bump_transaction::BumpTransactionEvent;
use lightning::events::Event as LdkEvent;
use lightning::events::PaymentPurpose;
use lightning::impl_writeable_tlv_based_enum;
Expand Down Expand Up @@ -283,6 +285,7 @@ where
{
event_queue: Arc<EventQueue<K, L>>,
wallet: Arc<Wallet>,
bump_tx_event_handler: Arc<BumpTransactionEventHandler>,
channel_manager: Arc<ChannelManager<K>>,
output_sweeper: Arc<Sweeper<K>>,
network_graph: Arc<NetworkGraph>,
Expand All @@ -300,13 +303,14 @@ where
pub fn new(
event_queue: Arc<EventQueue<K, L>>, wallet: Arc<Wallet>,
channel_manager: Arc<ChannelManager<K>>, output_sweeper: Arc<Sweeper<K>>,
network_graph: Arc<NetworkGraph>, payment_store: Arc<PaymentStore<K, L>>,
peer_store: Arc<PeerStore<K, L>>, runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
logger: L, config: Arc<Config>,
bump_tx_event_handler: Arc<BumpTransactionEventHandler>, network_graph: Arc<NetworkGraph>,
payment_store: Arc<PaymentStore<K, L>>, peer_store: Arc<PeerStore<K, L>>,
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>, logger: L, config: Arc<Config>,
) -> Self {
Self {
event_queue,
wallet,
bump_tx_event_handler,
channel_manager,
output_sweeper,
network_graph,
Expand Down Expand Up @@ -824,7 +828,40 @@ where
}
LdkEvent::DiscardFunding { .. } => {}
LdkEvent::HTLCIntercepted { .. } => {}
LdkEvent::BumpTransaction(_) => {}
LdkEvent::BumpTransaction(bte) => {
let (channel_id, counterparty_node_id) = match bte {
BumpTransactionEvent::ChannelClose {
ref channel_id,
ref counterparty_node_id,
..
} => (channel_id, counterparty_node_id),
BumpTransactionEvent::HTLCResolution {
ref channel_id,
ref counterparty_node_id,
..
} => (channel_id, counterparty_node_id),
};

if let Some(anchor_channels_config) = self.config.anchor_channels_config.as_ref() {
if anchor_channels_config
.trusted_peers_no_reserve
.contains(counterparty_node_id)
{
log_debug!(self.logger,
"Ignoring BumpTransactionEvent for channel {} due to trusted counterparty {}",
channel_id, counterparty_node_id
);
return;
}

self.bump_tx_event_handler.handle_event(&bte);
} else {
log_error!(self.logger,
"Ignoring BumpTransactionEvent for channel {} due to Anchor channels being unconfigured.",
channel_id,
);
};
}
LdkEvent::InvoiceRequestFailed { .. } => {}
LdkEvent::ConnectionNeeded { .. } => {}
}
Expand Down
13 changes: 11 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,15 @@ use payment_store::PaymentStore;
pub use payment_store::{PaymentDetails, PaymentDirection, PaymentStatus};
use peer_store::{PeerInfo, PeerStore};
use types::{
Broadcaster, ChainMonitor, ChannelManager, FeeEstimator, KeysManager, NetworkGraph,
PeerManager, Router, Scorer, Sweeper, Wallet,
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, FeeEstimator,
KeysManager, NetworkGraph, PeerManager, Router, Scorer, Sweeper, Wallet,
};
pub use types::{ChannelDetails, ChannelType, PeerDetails, UserChannelId};

use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger};

use lightning::chain::Confirm;
use lightning::events::bump_transaction::Wallet as LdkWallet;
use lightning::ln::channelmanager::{self, PaymentId, RecipientOnionFields, Retry};
use lightning::ln::msgs::SocketAddress;
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage};
Expand Down Expand Up @@ -588,11 +589,19 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
}
});

let bump_tx_event_handler = Arc::new(BumpTransactionEventHandler::new(
Arc::clone(&self.tx_broadcaster),
Arc::new(LdkWallet::new(Arc::clone(&self.wallet), Arc::clone(&self.logger))),
Arc::clone(&self.keys_manager),
Arc::clone(&self.logger),
));

let event_handler = Arc::new(EventHandler::new(
Arc::clone(&self.event_queue),
Arc::clone(&self.wallet),
Arc::clone(&self.channel_manager),
Arc::clone(&self.output_sweeper),
bump_tx_event_handler,
Arc::clone(&self.network_graph),
Arc::clone(&self.payment_store),
Arc::clone(&self.peer_store),
Expand Down
8 changes: 8 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ pub(crate) type Sweeper<K> = OutputSweeper<
Arc<FilesystemLogger>,
>;

pub(crate) type BumpTransactionEventHandler =
lightning::events::bump_transaction::BumpTransactionEventHandler<
Arc<Broadcaster>,
Arc<lightning::events::bump_transaction::Wallet<Arc<Wallet>, Arc<FilesystemLogger>>>,
Arc<KeysManager>,
Arc<FilesystemLogger>,
>;

/// A local, potentially user-provided, identifier of a channel.
///
/// By default, this will be randomly generated for the user to ensure local uniqueness.
Expand Down
126 changes: 122 additions & 4 deletions src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::Error;

use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};

use lightning::events::bump_transaction::{Utxo, WalletSource};
use lightning::ln::msgs::{DecodeError, UnsignedGossipMessage};
use lightning::ln::script::ShutdownScript;
use lightning::sign::{
Expand All @@ -19,8 +20,14 @@ use bdk::wallet::AddressIndex;
use bdk::FeeRate;
use bdk::{SignOptions, SyncOptions};

use bitcoin::address::{Payload, WitnessVersion};
use bitcoin::bech32::u5;
use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::hash_types::WPubkeyHash;
use bitcoin::hashes::Hash;
use bitcoin::key::XOnlyPublicKey;
use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, Signing};
Expand Down Expand Up @@ -245,6 +252,118 @@ where
}
}

impl<D, B: Deref, E: Deref, L: Deref> WalletSource for Wallet<D, B, E, L>
where
D: BatchDatabase,
B::Target: BroadcasterInterface,
E::Target: FeeEstimator,
L::Target: Logger,
{
fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()> {
let locked_wallet = self.inner.lock().unwrap();
let mut utxos = Vec::new();
let confirmed_txs: Vec<bdk::TransactionDetails> = locked_wallet
.list_transactions(false)
.map_err(|e| {
log_error!(self.logger, "Failed to retrieve transactions from wallet: {}", e);
})?
.into_iter()
.filter(|t| t.confirmation_time.is_some())
.collect();
let unspent_confirmed_utxos = locked_wallet
.list_unspent()
.map_err(|e| {
log_error!(
self.logger,
"Failed to retrieve unspent transactions from wallet: {}",
e
);
})?
.into_iter()
.filter(|u| confirmed_txs.iter().find(|t| t.txid == u.outpoint.txid).is_some());

for u in unspent_confirmed_utxos {
let payload = Payload::from_script(&u.txout.script_pubkey).map_err(|e| {
log_error!(self.logger, "Failed to retrieve script payload: {}", e);
})?;

match payload {
Payload::WitnessProgram(program) => match program.version() {
WitnessVersion::V0 if program.program().len() == 20 => {
let wpkh =
WPubkeyHash::from_slice(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);
}
WitnessVersion::V1 => {
XOnlyPublicKey::from_slice(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(&program),
},
satisfaction_weight: 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64 +
1 /* witness items */ + 1 /* schnorr sig len */ + 64, /* schnorr sig */
};
utxos.push(utxo);
}
_ => {
log_error!(
self.logger,
"Unexpected witness version or length. Version: {}, Length: {}",
program.version(),
program.program().len()
);
}
},
_ => {
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)
}

fn get_change_script(&self) -> Result<ScriptBuf, ()> {
let locked_wallet = self.inner.lock().unwrap();
let address_info = locked_wallet.get_address(AddressIndex::New).map_err(|e| {
log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e);
})?;

Ok(address_info.address.script_pubkey())
}

fn sign_psbt(&self, mut psbt: PartiallySignedTransaction) -> Result<Transaction, ()> {
let locked_wallet = self.inner.lock().unwrap();

match locked_wallet.sign(&mut psbt, SignOptions::default()) {
Ok(finalized) => {
if !finalized {
log_error!(self.logger, "Failed to finalize PSBT.");
return Err(());
}
}
Err(err) => {
log_error!(self.logger, "Failed to sign transaction: {}", err);
return Err(());
}
}

Ok(psbt.extract_tx())
}
}

/// Similar to [`KeysManager`], but overrides the destination and shutdown scripts so they are
/// directly spendable by the BDK wallet.
pub struct WalletKeysManager<D, B: Deref, E: Deref, L: Deref>
Expand Down Expand Up @@ -398,11 +517,10 @@ where
})?;

match address.payload {
bitcoin::address::Payload::WitnessProgram(program) => {
ShutdownScript::new_witness_program(&program).map_err(|e| {
Payload::WitnessProgram(program) => ShutdownScript::new_witness_program(&program)
.map_err(|e| {
log_error!(self.logger, "Invalid shutdown script: {:?}", e);
})
}
}),
_ => {
log_error!(
self.logger,
Expand Down

0 comments on commit 210ea82

Please sign in to comment.