Skip to content

Commit

Permalink
Require to manually set an entropy source
Browse files Browse the repository at this point in the history
Previously, we would default to auto-generating a `keys_seed` file in
the storage path. While this was convenient, especially in tests, it's
confusing for a lot of users and promotes questionable security
practices, i.e., storing key material on disk.

Here, we therefore require the user to manually specify an entropy
source. Users migrating from previous versions can simply migrate by
supplying the path to their `keys_seed` file via
`Builder::set_entropy_seed_file`.
  • Loading branch information
tnull committed Feb 1, 2024
1 parent c755eee commit 016bd63
Show file tree
Hide file tree
Showing 10 changed files with 47 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
@RunWith(AndroidJUnit4::class)
class AndroidLibTest {
@Test fun node_start_stop() {
val mnemonic1 = generateEntropyMnemonic()
val mnemonic2 = generateEntropyMnemonic()
val tmpDir1 = createTempDirectory("ldk_node").toString()
println("Random dir 1: $tmpDir1")
val tmpDir2 = createTempDirectory("ldk_node").toString()
Expand All @@ -37,7 +39,9 @@ class AndroidLibTest {
config2.logLevel = LogLevel.TRACE

val builder1 = Builder.fromConfig(config1)
builder1.setEntropyBip39Mnemonic(mnemonic1)
val builder2 = Builder.fromConfig(config2)
builder2.setEntropyBip39Mnemonic(mnemonic2)

val node1 = builder1.build()
val node2 = builder2.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ class LibraryTest {
val listenAddress1 = "127.0.0.1:2323"
val listenAddress2 = "127.0.0.1:2324"

val mnemonic1 = generateEntropyMnemonic()
val mnemonic2 = generateEntropyMnemonic()

val config1 = Config()
config1.storageDirPath = tmpDir1
config1.listeningAddresses = listOf(listenAddress1)
Expand All @@ -130,8 +133,10 @@ class LibraryTest {
println("Config 2: $config2")

val builder1 = Builder.fromConfig(config1)
builder1.setEntropyBip39Mnemonic(mnemonic1)
builder1.setEsploraServer(esploraEndpoint)
val builder2 = Builder.fromConfig(config2)
builder2.setEntropyBip39Mnemonic(mnemonic2)
builder2.setEsploraServer(esploraEndpoint)

val node1 = builder1.build()
Expand Down
1 change: 1 addition & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ enum BuildError {
"KVStoreSetupFailed",
"WalletSetupFailed",
"LoggerSetupFailed",
"EntropySourceRequired",
};

[Enum]
Expand Down
2 changes: 2 additions & 0 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ def send_to_address(address, amount_sats):


def setup_node(tmp_dir, esplora_endpoint, listening_addresses):
mnemonic = generate_entropy_mnemonic()
config = Config()
builder = Builder.from_config(config)
builder.set_entropy_source_bip39_mnemonic(mnemonic, nil)
builder.set_storage_dir_path(tmp_dir)
builder.set_esplora_server(esplora_endpoint)
builder.set_network(DEFAULT_TEST_NETWORK)
Expand Down
21 changes: 8 additions & 13 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ pub enum BuildError {
WalletSetupFailed,
/// We failed to setup the logger.
LoggerSetupFailed,
/// No entropy source was configured.
EntropySourceRequired,
}

impl fmt::Display for BuildError {
Expand All @@ -127,6 +129,7 @@ impl fmt::Display for BuildError {
Self::KVStoreSetupFailed => write!(f, "Failed to setup KVStore."),
Self::WalletSetupFailed => write!(f, "Failed to setup onchain wallet."),
Self::LoggerSetupFailed => write!(f, "Failed to setup the logger."),
Self::EntropySourceRequired => write!(f, "No entropy source was configured."),
}
}
}
Expand All @@ -137,7 +140,6 @@ impl std::error::Error for BuildError {}
/// the getgo.
///
/// ### Defaults
/// - Wallet entropy is sourced from a `keys_seed` file located under [`Config::storage_dir_path`]
/// - Chain data is sourced from the Esplora endpoint `https://blockstream.info/api`
/// - Gossip data is sourced via the peer-to-peer network
#[derive(Debug)]
Expand Down Expand Up @@ -329,11 +331,8 @@ impl NodeBuilder {
&self, kv_store: Arc<K>,
) -> Result<Node<K>, BuildError> {
let logger = setup_logger(&self.config)?;
let seed_bytes = seed_bytes_from_config(
&self.config,
self.entropy_source_config.as_ref(),
Arc::clone(&logger),
)?;
let seed_bytes =
seed_bytes_from_config(self.entropy_source_config.as_ref(), Arc::clone(&logger))?;
let config = Arc::new(self.config.clone());

build_with_store_internal(
Expand All @@ -351,7 +350,6 @@ impl NodeBuilder {
/// the getgo.
///
/// ### Defaults
/// - Wallet entropy is sourced from a `keys_seed` file located under [`Config::storage_dir_path`]
/// - Chain data is sourced from the Esplora endpoint `https://blockstream.info/api`
/// - Gossip data is sourced via the peer-to-peer network
#[derive(Debug)]
Expand Down Expand Up @@ -869,8 +867,7 @@ fn setup_logger(config: &Config) -> Result<Arc<FilesystemLogger>, BuildError> {
}

fn seed_bytes_from_config(
config: &Config, entropy_source_config: Option<&EntropySourceConfig>,
logger: Arc<FilesystemLogger>,
entropy_source_config: Option<&EntropySourceConfig>, logger: Arc<FilesystemLogger>,
) -> Result<[u8; 64], BuildError> {
match entropy_source_config {
Some(EntropySourceConfig::SeedBytes(bytes)) => Ok(bytes.clone()),
Expand All @@ -883,10 +880,8 @@ fn seed_bytes_from_config(
None => Ok(mnemonic.to_seed("")),
},
None => {
// Default to read or generate from the default location generate a seed file.
let seed_path = format!("{}/keys_seed", config.storage_dir_path);
Ok(io::utils::read_or_generate_seed_file(&seed_path, Arc::clone(&logger))
.map_err(|_| BuildError::InvalidSeedFile)?)
log_error!(logger, "Failed to set up Node as no entropy source was configured.");
Err(BuildError::EntropySourceRequired)
}
}
}
11 changes: 8 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,17 @@
//! [`send_payment`], etc.:
//!
//! ```no_run
//! use ldk_node::{Builder, Network};
//! use ldk_node::{Builder, Network, generate_entropy_mnemonic};
//! use ldk_node::lightning_invoice::Bolt11Invoice;
//! use ldk_node::lightning::ln::msgs::SocketAddress;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
//! let mnemonic = generate_entropy_mnemonic();
//!
//! let mut builder = Builder::new();
//! builder.set_entropy_bip39_mnemonic(mnemonic, None);
//! builder.set_network(Network::Testnet);
//! builder.set_esplora_server("https://blockstream.info/testnet/api".to_string());
//! builder.set_gossip_source_rgs("https://rapidsync.lightningdevkit.org/testnet/snapshot".to_string());
Expand Down Expand Up @@ -1547,11 +1550,13 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
///
/// For example, you could retrieve all stored outbound payments as follows:
/// ```
/// # use ldk_node::{Builder, Config, Network, PaymentDirection};
/// # use ldk_node::{Builder, Config, Network, PaymentDirection, generate_entropy_mnemonic};
/// # let mnemonic = generate_entropy_mnemonic();
/// # let mut config = Config::default();
/// # config.network = Network::Regtest;
/// # config.storage_dir_path = "/tmp/ldk_node_test/".to_string();
/// # let builder = Builder::from_config(config);
/// # let mut builder = Builder::from_config(config);
/// # builder.set_entropy_bip39_mnemonic(mnemonic, None);
/// # let node = builder.build().unwrap();
/// node.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound);
/// ```
Expand Down
5 changes: 4 additions & 1 deletion tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

use ldk_node::io::sqlite_store::SqliteStore;
use ldk_node::{
Builder, Config, Event, LogLevel, Network, Node, NodeError, PaymentDirection, PaymentStatus,
generate_entropy_mnemonic, Builder, Config, Event, LogLevel, Network, Node, NodeError,
PaymentDirection, PaymentStatus,
};

use lightning::ln::msgs::SocketAddress;
Expand Down Expand Up @@ -178,8 +179,10 @@ pub(crate) fn setup_two_nodes(
}

pub(crate) fn setup_node(electrsd: &ElectrsD, config: Config) -> TestNode<TestSyncStore> {
let mnemonic = generate_entropy_mnemonic();
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
setup_builder!(builder, config);
builder.set_entropy_bip39_mnemonic(mnemonic, None);
builder.set_esplora_server(esplora_url.clone());
let test_sync_store = Arc::new(TestSyncStore::new(config.storage_dir_path.into()));
let node = builder.build_with_store(test_sync_store).unwrap();
Expand Down
2 changes: 2 additions & 0 deletions tests/integration_tests_cln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ fn test_cln() {
common::generate_blocks_and_wait(&bitcoind_client, &electrs_client, 1);

// Setup LDK Node
let mnemonic = generate_entropy_mnemonic();
let config = common::random_config();
let mut builder = Builder::from_config(config);
builder.set_esplora_server("http://127.0.0.1:3002".to_string());
builder.set_entropy_bip39_mnemonic(mnemonic, None);

let node = builder.build().unwrap();
node.start().unwrap();
Expand Down
9 changes: 8 additions & 1 deletion tests/integration_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use common::{
setup_node, setup_two_nodes, wait_for_tx, TestSyncStore,
};

use ldk_node::{Builder, Event, Network, NodeError};
use ldk_node::{generate_entropy_mnemonic, Builder, Event, Network, NodeError};

use bitcoin::Amount;

Expand Down Expand Up @@ -69,8 +69,10 @@ fn multi_hop_sending() {
// Setup and fund 5 nodes
let mut nodes = Vec::new();
for _ in 0..5 {
let mnemonic = generate_entropy_mnemonic();
let config = random_config();
setup_builder!(builder, config);
builder.set_entropy_bip39_mnemonic(mnemonic, None);
builder.set_esplora_server(esplora_url.clone());
let node = builder.build().unwrap();
node.start().unwrap();
Expand Down Expand Up @@ -139,9 +141,11 @@ fn multi_hop_sending() {

#[test]
fn connect_to_public_testnet_esplora() {
let mnemonic = generate_entropy_mnemonic();
let mut config = random_config();
config.network = Network::Testnet;
setup_builder!(builder, config);
builder.set_entropy_bip39_mnemonic(mnemonic, None);
builder.set_esplora_server("https://blockstream.info/testnet/api".to_string());
let node = builder.build().unwrap();
node.start().unwrap();
Expand All @@ -151,13 +155,15 @@ fn connect_to_public_testnet_esplora() {
#[test]
fn start_stop_reinit() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let mnemonic = generate_entropy_mnemonic();
let config = random_config();

let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());

let test_sync_store = Arc::new(TestSyncStore::new(config.storage_dir_path.clone().into()));

setup_builder!(builder, config);
builder.set_entropy_bip39_mnemonic(mnemonic.clone(), None);
builder.set_esplora_server(esplora_url.clone());

let node = builder.build_with_store(Arc::clone(&test_sync_store)).unwrap();
Expand Down Expand Up @@ -195,6 +201,7 @@ fn start_stop_reinit() {
drop(node);

setup_builder!(builder, config);
builder.set_entropy_bip39_mnemonic(mnemonic, None);
builder.set_esplora_server(esplora_url.clone());

let reinitialized_node = builder.build_with_store(Arc::clone(&test_sync_store)).unwrap();
Expand Down
6 changes: 5 additions & 1 deletion tests/integration_tests_vss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@

mod common;

use ldk_node::Builder;
use ldk_node::{generate_entropy_mnemonic, Builder};

#[test]
fn channel_full_cycle_with_vss_store() {
let (bitcoind, electrsd) = common::setup_bitcoind_and_electrsd();
println!("== Node A ==");
let mnemonic_a = generate_entropy_mnemonic();
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
let config_a = common::random_config();
let mut builder_a = Builder::from_config(config_a);
builder_a.set_entropy_bip39_mnemnonic(mnemonic_a, None);
builder_a.set_esplora_server(esplora_url.clone());
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
let node_a =
builder_a.build_with_vss_store(vss_base_url.clone(), "node_1_store".to_string()).unwrap();
node_a.start().unwrap();

println!("\n== Node B ==");
let mnemonic_b = generate_entropy_mnemonic();
let config_b = common::random_config();
let mut builder_b = Builder::from_config(config_b);
builder_b.set_entropy_bip39_mnemnonic(mnemonic_b, None);
builder_b.set_esplora_server(esplora_url);
let node_b = builder_b.build_with_vss_store(vss_base_url, "node_2_store".to_string()).unwrap();
node_b.start().unwrap();
Expand Down

0 comments on commit 016bd63

Please sign in to comment.