Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Custom Key Manager #396

Merged
merged 1 commit into from
Apr 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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