Skip to content

Commit bc63f83

Browse files
committed
Implement WalletKeysManager
This implementation of `KeysInterface` allows to override the shutdown/destination scripts and forwards any other calls to an inner instance of `KeysManager` otherwise.
1 parent 4414f5e commit bc63f83

File tree

1 file changed

+138
-1
lines changed

1 file changed

+138
-1
lines changed

src/wallet.rs

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,29 @@
11
use crate::logger::{
22
log_error, log_given_level, log_internal, log_trace, FilesystemLogger, Logger,
33
};
4+
45
use crate::Error;
56

67
use lightning::chain::chaininterface::{
78
BroadcasterInterface, ConfirmationTarget, FeeEstimator, FEERATE_FLOOR_SATS_PER_KW,
89
};
910

11+
use lightning::chain::keysinterface::{
12+
InMemorySigner, KeyMaterial, KeysInterface, KeysManager, Recipient, SpendableOutputDescriptor,
13+
};
14+
use lightning::ln::msgs::DecodeError;
15+
use lightning::ln::script::ShutdownScript;
16+
1017
use bdk::blockchain::{Blockchain, EsploraBlockchain};
1118
use bdk::database::BatchDatabase;
1219
use bdk::wallet::AddressIndex;
1320
use bdk::{FeeRate, SignOptions, SyncOptions};
1421

15-
use bitcoin::{Script, Transaction};
22+
use bitcoin::bech32::u5;
23+
use bitcoin::secp256k1::ecdh::SharedSecret;
24+
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
25+
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, Signing};
26+
use bitcoin::{Script, Transaction, TxOut};
1627

1728
use std::collections::HashMap;
1829
use std::sync::{Arc, Mutex, RwLock};
@@ -105,6 +116,11 @@ where
105116
Ok(address_info.address)
106117
}
107118

119+
#[cfg(any(test))]
120+
pub(crate) fn get_balance(&self) -> Result<bdk::Balance, Error> {
121+
Ok(self.wallet.lock().unwrap().get_balance()?)
122+
}
123+
108124
fn estimate_fee_rate(&self, confirmation_target: ConfirmationTarget) -> FeeRate {
109125
let mut locked_fee_rate_cache = self.fee_rate_cache.lock().unwrap();
110126
let num_blocks = num_blocks_from_conf_target(confirmation_target);
@@ -199,3 +215,124 @@ fn fallback_fee_from_conf_target(confirmation_target: ConfirmationTarget) -> Fee
199215

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

0 commit comments

Comments
 (0)