Skip to content

Commit

Permalink
Create Custom Key Manager (#396)
Browse files Browse the repository at this point in the history
  • Loading branch information
benthecarman committed Apr 22, 2023
1 parent 3bad357 commit 34fadc8
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 27 deletions.
13 changes: 1 addition & 12 deletions mutiny-core/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use crate::fees::MutinyFeeEstimator;
use crate::keymanager::PhantomKeysManager;
use crate::ldkstorage::{MutinyNodePersister, PhantomChannelManager};
use crate::logging::MutinyLogger;
use crate::utils::sleep;
use crate::wallet::MutinyWallet;
use bdk::wallet::AddressIndex;
use bitcoin::hashes::hex::ToHex;
use bitcoin::secp256k1::PublicKey;
use bitcoin::secp256k1::Secp256k1;
use lightning::{
chain::chaininterface::{ConfirmationTarget, FeeEstimator},
chain::keysinterface::PhantomKeysManager,
util::errors::APIError,
util::{
events::{Event, PaymentPurpose},
Expand Down Expand Up @@ -514,15 +513,6 @@ impl EventHandler {
"",
0,
));
// Do lock inside of bracket to avoid holding it for too long
let address = {
let mut wallet = self
.wallet
.wallet
.try_write()
.expect("failed to lock wallet");
wallet.get_internal_address(AddressIndex::New).address
};

let output_descriptors = &outputs.iter().collect::<Vec<_>>();
let tx_feerate = self
Expand All @@ -533,7 +523,6 @@ impl EventHandler {
.spend_spendable_outputs(
output_descriptors,
Vec::new(),
address.script_pubkey(),
tx_feerate,
&Secp256k1::new(),
)
Expand Down
179 changes: 168 additions & 11 deletions mutiny-core/src/keymanager.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,147 @@
use crate::error::MutinyError;
use crate::wallet::MutinyWallet;
use bdk::wallet::AddressIndex;
use bip32::XPrv;
use bip39::Mnemonic;
use bitcoin::secp256k1::{PublicKey, Secp256k1};
use lightning::chain::keysinterface::{EntropySource, NodeSigner, PhantomKeysManager, Recipient};
use bitcoin::bech32::u5;
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use bitcoin::secp256k1::ecdsa::Signature;
use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, Signing};
use bitcoin::{Script, Transaction, TxOut};
use lightning::chain::keysinterface::{
EntropySource, InMemorySigner, KeyMaterial, NodeSigner,
PhantomKeysManager as LdkPhantomKeysManager, Recipient, SignerProvider,
SpendableOutputDescriptor,
};
use lightning::ln::msgs::{DecodeError, UnsignedGossipMessage};
use lightning::ln::script::ShutdownScript;
use std::sync::Arc;

pub struct PhantomKeysManager {
inner: LdkPhantomKeysManager,
wallet: Arc<MutinyWallet>,
}

impl PhantomKeysManager {
pub fn new(
wallet: Arc<MutinyWallet>,
seed: &[u8; 32],
starting_time_secs: u64,
starting_time_nanos: u32,
cross_node_seed: &[u8; 32],
) -> Self {
let inner = LdkPhantomKeysManager::new(
seed,
starting_time_secs,
starting_time_nanos,
cross_node_seed,
);
Self { inner, wallet }
}

/// See [`KeysManager::spend_spendable_outputs`] for documentation on this method.
pub fn spend_spendable_outputs<C: Signing>(
&self,
descriptors: &[&SpendableOutputDescriptor],
outputs: Vec<TxOut>,
feerate_sat_per_1000_weight: u32,
secp_ctx: &Secp256k1<C>,
) -> Result<Transaction, ()> {
let mut wallet = self.wallet.wallet.try_write().map_err(|_| ())?;
let address = wallet.get_internal_address(AddressIndex::New).address;

self.inner.spend_spendable_outputs(
descriptors,
outputs,
address.script_pubkey(),
feerate_sat_per_1000_weight,
secp_ctx,
)
}
}

impl EntropySource for PhantomKeysManager {
fn get_secure_random_bytes(&self) -> [u8; 32] {
self.inner.get_secure_random_bytes()
}
}

impl NodeSigner for PhantomKeysManager {
fn get_inbound_payment_key_material(&self) -> KeyMaterial {
self.inner.get_inbound_payment_key_material()
}

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 sign_invoice(
&self,
hrp_bytes: &[u8],
invoice_data: &[u5],
recipient: Recipient,
) -> Result<RecoverableSignature, ()> {
self.inner.sign_invoice(hrp_bytes, invoice_data, recipient)
}

fn sign_gossip_message(&self, msg: UnsignedGossipMessage) -> Result<Signature, ()> {
self.inner.sign_gossip_message(msg)
}
}

impl SignerProvider for PhantomKeysManager {
type Signer = InMemorySigner;

fn generate_channel_keys_id(
&self,
inbound: bool,
channel_value_satoshis: u64,
user_channel_id: u128,
) -> [u8; 32] {
self.inner
.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id)
}

fn derive_channel_signer(
&self,
channel_value_satoshis: u64,
channel_keys_id: [u8; 32],
) -> Self::Signer {
self.inner
.derive_channel_signer(channel_value_satoshis, channel_keys_id)
}

fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, DecodeError> {
self.inner.read_chan_signer(reader)
}

fn get_destination_script(&self) -> Script {
let mut wallet = self.wallet.wallet.try_write().unwrap();
wallet
.get_address(AddressIndex::New)
.address
.script_pubkey()
}

fn get_shutdown_scriptpubkey(&self) -> ShutdownScript {
let mut wallet = self.wallet.wallet.try_write().unwrap();
let script = wallet
.get_address(AddressIndex::New)
.address
.script_pubkey();
ShutdownScript::try_from(script).unwrap()
}
}

pub(crate) fn generate_seed(num_words: u8) -> Result<Mnemonic, MutinyError> {
match num_words {
Expand Down Expand Up @@ -31,7 +170,11 @@ fn generate_12_word_seed() -> Result<Mnemonic, MutinyError> {
// A node private key will be derived from `m/0'/X'`, where its node pubkey will
// be derived from the LDK default being `m/0'/X'/0'`. The PhantomKeysManager shared
// key secret will be derived from `m/0'`.
pub(crate) fn create_keys_manager(mnemonic: &Mnemonic, child_index: u32) -> PhantomKeysManager {
pub(crate) fn create_keys_manager(
wallet: Arc<MutinyWallet>,
mnemonic: &Mnemonic,
child_index: u32,
) -> PhantomKeysManager {
let shared_key = XPrv::new(mnemonic.to_seed(""))
.unwrap()
.derive_child(bip32::ChildNumber::new(0, true).unwrap())
Expand All @@ -44,17 +187,15 @@ pub(crate) fn create_keys_manager(mnemonic: &Mnemonic, child_index: u32) -> Phan
let now = crate::utils::now();

PhantomKeysManager::new(
wallet,
&xpriv.to_bytes(),
now.as_secs(),
(now.as_nanos() % (1 << 31)) as u32, // temp fix for https://github.com/lightningdevkit/rust-lightning/pull/1935
now.as_nanos() as u32,
&shared_key.to_bytes(),
)
}

pub(crate) fn pubkey_from_keys_manager(keys_manager: &PhantomKeysManager) -> PublicKey {
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());

keys_manager
.get_node_id(Recipient::Node)
.expect("cannot parse node id")
Expand All @@ -69,30 +210,46 @@ mod tests {
use crate::{keymanager::pubkey_from_keys_manager, test_utils::*};

use super::create_keys_manager;
use crate::indexed_db::MutinyStorage;
use crate::wallet::MutinyWallet;
use bip39::Mnemonic;
use bitcoin::Network;
use esplora_client::{AsyncClient, Builder};
use std::str::FromStr;
use std::sync::Arc;

#[test]
fn derive_pubkey_child_from_seed() {
async fn derive_pubkey_child_from_seed() {
log!("creating pubkeys from a child seed");

let mnemonic = Mnemonic::from_str("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").expect("could not generate");
let esplora = Builder::new("https://blockstream.info/testnet/api/")
.build_async()
.unwrap();
let db = MutinyStorage::new("".to_string()).await.unwrap();

let wallet = Arc::new(MutinyWallet::new(
&mnemonic,
db,
Network::Testnet,
Arc::new(esplora),
));

let km = create_keys_manager(&mnemonic, 1);
let km = create_keys_manager(wallet.clone(), &mnemonic, 1);
let pubkey = pubkey_from_keys_manager(&km);
assert_eq!(
"02cae09cf2c8842ace44068a5bf3117a494ebbf69a99e79712483c36f97cdb7b54",
pubkey.to_string()
);

let km = create_keys_manager(&mnemonic, 2);
let km = create_keys_manager(wallet.clone(), &mnemonic, 2);
let second_pubkey = pubkey_from_keys_manager(&km);
assert_eq!(
"03fcc9eaaf0b84946ea7935e3bc4f2b498893c2f53e5d2994d6877d149601ce553",
second_pubkey.to_string()
);

let km = create_keys_manager(&mnemonic, 2);
let km = create_keys_manager(wallet, &mnemonic, 2);
let second_pubkey_again = pubkey_from_keys_manager(&km);

assert_eq!(second_pubkey, second_pubkey_again);
Expand Down
2 changes: 1 addition & 1 deletion mutiny-core/src/ldkstorage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::event::PaymentInfo;
use crate::fees::MutinyFeeEstimator;
use crate::gossip;
use crate::indexed_db::MutinyStorage;
use crate::keymanager::PhantomKeysManager;
use crate::logging::MutinyLogger;
use crate::node::{default_user_config, ChainMonitor, ProbScorer};
use crate::node::{NetworkGraph, Router};
Expand All @@ -17,7 +18,6 @@ use futures::{try_join, TryFutureExt};
use lightning::chain;
use lightning::chain::chainmonitor::{MonitorUpdateId, Persist};
use lightning::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate};
use lightning::chain::keysinterface::PhantomKeysManager;
use lightning::chain::keysinterface::{InMemorySigner, WriteableEcdsaChannelSigner};
use lightning::chain::transaction::OutPoint;
use lightning::chain::BestBlock;
Expand Down
9 changes: 7 additions & 2 deletions mutiny-core/src/node.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::indexed_db::MutinyStorage;
use crate::keymanager::PhantomKeysManager;
use crate::{
background::process_events_async,
chain::MutinyChain,
Expand Down Expand Up @@ -29,7 +30,7 @@ use bitcoin::{hashes::Hash, secp256k1::PublicKey, Network};
use lightning::{
chain::{
chainmonitor,
keysinterface::{EntropySource, InMemorySigner, PhantomKeysManager},
keysinterface::{EntropySource, InMemorySigner},
Filter, Watch,
},
ln::{
Expand Down Expand Up @@ -165,7 +166,11 @@ impl Node {

let logger = Arc::new(MutinyLogger::default());

let keys_manager = Arc::new(create_keys_manager(mnemonic, node_index.child_index));
let keys_manager = Arc::new(create_keys_manager(
wallet.clone(),
mnemonic,
node_index.child_index,
));
let pubkey = pubkey_from_keys_manager(&keys_manager);

// init the persister
Expand Down
2 changes: 1 addition & 1 deletion mutiny-core/src/peermanager.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::gossip::read_peer_info;
use crate::keymanager::PhantomKeysManager;
use crate::node::NetworkGraph;
use crate::{
gossip, ldkstorage::PhantomChannelManager, logging::MutinyLogger, socket::WsSocketDescriptor,
};
use bitcoin::secp256k1::PublicKey;
use bitcoin::BlockHash;
use lightning::chain::keysinterface::PhantomKeysManager;
use lightning::ln::features::{InitFeatures, NodeFeatures};
use lightning::ln::msgs;
use lightning::ln::msgs::{LightningError, NetAddress, RoutingMessageHandler};
Expand Down

0 comments on commit 34fadc8

Please sign in to comment.