|
1 | 1 | use crate::logger::{
|
2 | 2 | log_error, log_given_level, log_internal, log_trace, FilesystemLogger, Logger,
|
3 | 3 | };
|
| 4 | + |
4 | 5 | use crate::Error;
|
5 | 6 |
|
6 | 7 | use lightning::chain::chaininterface::{
|
7 | 8 | BroadcasterInterface, ConfirmationTarget, FeeEstimator, FEERATE_FLOOR_SATS_PER_KW,
|
8 | 9 | };
|
9 | 10 |
|
| 11 | +use lightning::chain::keysinterface::{ |
| 12 | + EntropySource, InMemorySigner, KeyMaterial, KeysInterface, KeysManager, NodeSigner, Recipient, |
| 13 | + SignerProvider, SpendableOutputDescriptor, |
| 14 | +}; |
| 15 | +use lightning::ln::msgs::DecodeError; |
| 16 | +use lightning::ln::script::ShutdownScript; |
| 17 | + |
10 | 18 | use bdk::blockchain::{Blockchain, EsploraBlockchain};
|
11 | 19 | use bdk::database::BatchDatabase;
|
12 | 20 | use bdk::wallet::AddressIndex;
|
13 | 21 | use bdk::{FeeRate, SignOptions, SyncOptions};
|
14 | 22 |
|
15 |
| -use bitcoin::{Script, Transaction}; |
| 23 | +use bitcoin::bech32::u5; |
| 24 | +use bitcoin::secp256k1::ecdh::SharedSecret; |
| 25 | +use bitcoin::secp256k1::ecdsa::RecoverableSignature; |
| 26 | +use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, Signing}; |
| 27 | +use bitcoin::{Script, Transaction, TxOut}; |
16 | 28 |
|
17 | 29 | use std::collections::HashMap;
|
18 | 30 | use std::sync::{Arc, Mutex, RwLock};
|
@@ -212,3 +224,136 @@ fn fallback_fee_from_conf_target(confirmation_target: ConfirmationTarget) -> Fee
|
212 | 224 |
|
213 | 225 | FeeRate::from_sat_per_kwu(sats_kwu as f32)
|
214 | 226 | }
|
| 227 | + |
| 228 | +/// Similar to [`KeysManager`], but overrides the destination and shutdown scripts so they are |
| 229 | +/// directly spendable by the BDK wallet. |
| 230 | +pub struct WalletKeysManager<D> |
| 231 | +where |
| 232 | + D: BatchDatabase, |
| 233 | +{ |
| 234 | + inner: KeysManager, |
| 235 | + wallet: Arc<Wallet<D>>, |
| 236 | +} |
| 237 | + |
| 238 | +impl<D> WalletKeysManager<D> |
| 239 | +where |
| 240 | + D: BatchDatabase, |
| 241 | +{ |
| 242 | + /// Constructs a `WalletKeysManager` that |
| 243 | + /// |
| 244 | + /// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, and |
| 245 | + /// `starting_time_nanos`. |
| 246 | + pub fn new( |
| 247 | + seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, wallet: Arc<Wallet<D>>, |
| 248 | + ) -> Self { |
| 249 | + let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos); |
| 250 | + Self { inner, wallet } |
| 251 | + } |
| 252 | + |
| 253 | + /// See [`KeysManager::spend_spendable_outputs`] for documentation on this method. |
| 254 | + pub fn spend_spendable_outputs<C: Signing>( |
| 255 | + &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, |
| 256 | + change_destination_script: Script, feerate_sat_per_1000_weight: u32, |
| 257 | + secp_ctx: &Secp256k1<C>, |
| 258 | + ) -> Result<Transaction, ()> { |
| 259 | + let only_non_static = &descriptors |
| 260 | + .iter() |
| 261 | + .filter(|desc| { |
| 262 | + if let SpendableOutputDescriptor::StaticOutput { .. } = desc { |
| 263 | + false |
| 264 | + } else { |
| 265 | + true |
| 266 | + } |
| 267 | + }) |
| 268 | + .copied() |
| 269 | + .collect::<Vec<_>>(); |
| 270 | + self.inner.spend_spendable_outputs( |
| 271 | + only_non_static, |
| 272 | + outputs, |
| 273 | + change_destination_script, |
| 274 | + feerate_sat_per_1000_weight, |
| 275 | + secp_ctx, |
| 276 | + ) |
| 277 | + } |
| 278 | +} |
| 279 | + |
| 280 | +impl<D> NodeSigner for WalletKeysManager<D> |
| 281 | +where |
| 282 | + D: BatchDatabase, |
| 283 | +{ |
| 284 | + fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> { |
| 285 | + self.inner.get_node_secret(recipient) |
| 286 | + } |
| 287 | + |
| 288 | + fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> { |
| 289 | + self.inner.get_node_id(recipient) |
| 290 | + } |
| 291 | + |
| 292 | + fn ecdh( |
| 293 | + &self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>, |
| 294 | + ) -> Result<SharedSecret, ()> { |
| 295 | + self.inner.ecdh(recipient, other_key, tweak) |
| 296 | + } |
| 297 | + |
| 298 | + fn get_inbound_payment_key_material(&self) -> KeyMaterial { |
| 299 | + self.inner.get_inbound_payment_key_material() |
| 300 | + } |
| 301 | + |
| 302 | + fn sign_invoice( |
| 303 | + &self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient, |
| 304 | + ) -> Result<RecoverableSignature, ()> { |
| 305 | + self.inner.sign_invoice(hrp_bytes, invoice_data, recipient) |
| 306 | + } |
| 307 | +} |
| 308 | + |
| 309 | +impl<D> KeysInterface for WalletKeysManager<D> where D: BatchDatabase {} |
| 310 | + |
| 311 | +impl<D> EntropySource for WalletKeysManager<D> |
| 312 | +where |
| 313 | + D: BatchDatabase, |
| 314 | +{ |
| 315 | + fn get_secure_random_bytes(&self) -> [u8; 32] { |
| 316 | + self.inner.get_secure_random_bytes() |
| 317 | + } |
| 318 | +} |
| 319 | + |
| 320 | +impl<D> SignerProvider for WalletKeysManager<D> |
| 321 | +where |
| 322 | + D: BatchDatabase, |
| 323 | +{ |
| 324 | + type Signer = InMemorySigner; |
| 325 | + |
| 326 | + fn generate_channel_keys_id( |
| 327 | + &self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128, |
| 328 | + ) -> [u8; 32] { |
| 329 | + self.inner.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id) |
| 330 | + } |
| 331 | + |
| 332 | + fn derive_channel_signer( |
| 333 | + &self, channel_value_satoshis: u64, channel_keys_id: [u8; 32], |
| 334 | + ) -> Self::Signer { |
| 335 | + self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id) |
| 336 | + } |
| 337 | + |
| 338 | + fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> { |
| 339 | + self.inner.read_chan_signer(reader) |
| 340 | + } |
| 341 | + |
| 342 | + fn get_destination_script(&self) -> Script { |
| 343 | + let address = |
| 344 | + self.wallet.get_new_address().expect("Failed to retrieve new address from wallet."); |
| 345 | + address.script_pubkey() |
| 346 | + } |
| 347 | + |
| 348 | + fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { |
| 349 | + let address = |
| 350 | + self.wallet.get_new_address().expect("Failed to retrieve new address from wallet."); |
| 351 | + match address.payload { |
| 352 | + bitcoin::util::address::Payload::WitnessProgram { version, program } => { |
| 353 | + return ShutdownScript::new_witness_program(version, &program) |
| 354 | + .expect("Invalid shutdown script."); |
| 355 | + } |
| 356 | + _ => panic!("Tried to use a non-witness address. This must not ever happen."), |
| 357 | + } |
| 358 | + } |
| 359 | +} |
0 commit comments