|
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};
|
@@ -195,3 +207,136 @@ where
|
195 | 207 | }
|
196 | 208 | }
|
197 | 209 | }
|
| 210 | + |
| 211 | +/// Similar to [`KeysManager`], but overrides the destination and shutdown scripts so they are |
| 212 | +/// directly spendable by the BDK wallet. |
| 213 | +pub struct WalletKeysManager<D> |
| 214 | +where |
| 215 | + D: BatchDatabase, |
| 216 | +{ |
| 217 | + inner: KeysManager, |
| 218 | + wallet: Arc<Wallet<D>>, |
| 219 | +} |
| 220 | + |
| 221 | +impl<D> WalletKeysManager<D> |
| 222 | +where |
| 223 | + D: BatchDatabase, |
| 224 | +{ |
| 225 | + /// Constructs a `WalletKeysManager` that |
| 226 | + /// |
| 227 | + /// See [`KeysManager::new`] for more information on `seed`, `starting_time_secs`, and |
| 228 | + /// `starting_time_nanos`. |
| 229 | + pub fn new( |
| 230 | + seed: &[u8; 32], starting_time_secs: u64, starting_time_nanos: u32, wallet: Arc<Wallet<D>>, |
| 231 | + ) -> Self { |
| 232 | + let inner = KeysManager::new(seed, starting_time_secs, starting_time_nanos); |
| 233 | + Self { inner, wallet } |
| 234 | + } |
| 235 | + |
| 236 | + /// See [`KeysManager::spend_spendable_outputs`] for documentation on this method. |
| 237 | + pub fn spend_spendable_outputs<C: Signing>( |
| 238 | + &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec<TxOut>, |
| 239 | + change_destination_script: Script, feerate_sat_per_1000_weight: u32, |
| 240 | + secp_ctx: &Secp256k1<C>, |
| 241 | + ) -> Result<Transaction, ()> { |
| 242 | + let only_non_static = &descriptors |
| 243 | + .iter() |
| 244 | + .filter(|desc| { |
| 245 | + if let SpendableOutputDescriptor::StaticOutput { .. } = desc { |
| 246 | + false |
| 247 | + } else { |
| 248 | + true |
| 249 | + } |
| 250 | + }) |
| 251 | + .copied() |
| 252 | + .collect::<Vec<_>>(); |
| 253 | + self.inner.spend_spendable_outputs( |
| 254 | + only_non_static, |
| 255 | + outputs, |
| 256 | + change_destination_script, |
| 257 | + feerate_sat_per_1000_weight, |
| 258 | + secp_ctx, |
| 259 | + ) |
| 260 | + } |
| 261 | +} |
| 262 | + |
| 263 | +impl<D> NodeSigner for WalletKeysManager<D> |
| 264 | +where |
| 265 | + D: BatchDatabase, |
| 266 | +{ |
| 267 | + fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()> { |
| 268 | + self.inner.get_node_secret(recipient) |
| 269 | + } |
| 270 | + |
| 271 | + fn get_node_id(&self, recipient: Recipient) -> Result<PublicKey, ()> { |
| 272 | + self.inner.get_node_id(recipient) |
| 273 | + } |
| 274 | + |
| 275 | + fn ecdh( |
| 276 | + &self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>, |
| 277 | + ) -> Result<SharedSecret, ()> { |
| 278 | + self.inner.ecdh(recipient, other_key, tweak) |
| 279 | + } |
| 280 | + |
| 281 | + fn get_inbound_payment_key_material(&self) -> KeyMaterial { |
| 282 | + self.inner.get_inbound_payment_key_material() |
| 283 | + } |
| 284 | + |
| 285 | + fn sign_invoice( |
| 286 | + &self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient, |
| 287 | + ) -> Result<RecoverableSignature, ()> { |
| 288 | + self.inner.sign_invoice(hrp_bytes, invoice_data, recipient) |
| 289 | + } |
| 290 | +} |
| 291 | + |
| 292 | +impl<D> KeysInterface for WalletKeysManager<D> where D: BatchDatabase {} |
| 293 | + |
| 294 | +impl<D> EntropySource for WalletKeysManager<D> |
| 295 | +where |
| 296 | + D: BatchDatabase, |
| 297 | +{ |
| 298 | + fn get_secure_random_bytes(&self) -> [u8; 32] { |
| 299 | + self.inner.get_secure_random_bytes() |
| 300 | + } |
| 301 | +} |
| 302 | + |
| 303 | +impl<D> SignerProvider for WalletKeysManager<D> |
| 304 | +where |
| 305 | + D: BatchDatabase, |
| 306 | +{ |
| 307 | + type Signer = InMemorySigner; |
| 308 | + |
| 309 | + fn generate_channel_keys_id( |
| 310 | + &self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128, |
| 311 | + ) -> [u8; 32] { |
| 312 | + self.inner.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id) |
| 313 | + } |
| 314 | + |
| 315 | + fn derive_channel_signer( |
| 316 | + &self, channel_value_satoshis: u64, channel_keys_id: [u8; 32], |
| 317 | + ) -> Self::Signer { |
| 318 | + self.inner.derive_channel_signer(channel_value_satoshis, channel_keys_id) |
| 319 | + } |
| 320 | + |
| 321 | + fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> { |
| 322 | + self.inner.read_chan_signer(reader) |
| 323 | + } |
| 324 | + |
| 325 | + fn get_destination_script(&self) -> Script { |
| 326 | + let address = |
| 327 | + self.wallet.get_new_address().expect("Failed to retrieve new address from wallet."); |
| 328 | + address.script_pubkey() |
| 329 | + } |
| 330 | + |
| 331 | + fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { |
| 332 | + let address = |
| 333 | + self.wallet.get_new_address().expect("Failed to retrieve new address from wallet."); |
| 334 | + match address.payload { |
| 335 | + bitcoin::util::address::Payload::WitnessProgram { version, program } => { |
| 336 | + return ShutdownScript::new_witness_program(version, &program) |
| 337 | + .expect("Invalid shutdown script."); |
| 338 | + } |
| 339 | + _ => panic!("Tried to use a non-witness address. This must not ever happen."), |
| 340 | + } |
| 341 | + } |
| 342 | +} |
0 commit comments