Skip to content

Commit

Permalink
Merge pull request #56 from getAlby/reset-router-on-build
Browse files Browse the repository at this point in the history
Implement persistent state reset on node startup
  • Loading branch information
rolznz authored Nov 30, 2024
2 parents 3ba4e8a + f61295a commit 1c7508f
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 2 deletions.
8 changes: 8 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ interface Builder {
[Throws=BuildError]
Node build_with_fs_store();
void restore_encoded_channel_monitors(sequence<KeyValue> monitors);
void reset_state(ResetState what);
[Throws=BuildError]
Node build_with_vss_store(string vss_url, string store_id, string lnurl_auth_server_url, record<string, string> fixed_headers);
[Throws=BuildError]
Expand Down Expand Up @@ -643,3 +644,10 @@ typedef string UntrustedString;

[Custom]
typedef string NodeAlias;

enum ResetState {
"NodeMetrics",
"Scorer",
"NetworkGraph",
"All",
};
78 changes: 76 additions & 2 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use crate::gossip::GossipSource;
use crate::io::sqlite_store::SqliteStore;
use crate::io::utils::{read_node_metrics, write_node_metrics};
use crate::io::vss_store::VssStore;
use crate::io::{
NODE_METRICS_KEY, NODE_METRICS_PRIMARY_NAMESPACE, NODE_METRICS_SECONDARY_NAMESPACE,
};
use crate::liquidity::LiquiditySource;
use crate::logger::{log_error, log_info, FilesystemLogger, Logger};
use crate::message_handler::NodeCustomMessageHandler;
Expand All @@ -23,7 +26,7 @@ use crate::peer_store::PeerStore;
use crate::tx_broadcaster::TransactionBroadcaster;
use crate::types::{
ChainMonitor, ChannelManager, DynStore, GossipSync, Graph, KeyValue, KeysManager,
MessageRouter, OnionMessenger, PeerManager,
MessageRouter, OnionMessenger, PeerManager, ResetState,
};
use crate::wallet::persist::KVStoreWalletPersister;
use crate::wallet::Wallet;
Expand All @@ -45,6 +48,9 @@ use lightning::sign::EntropySource;
use lightning::util::persist::{
read_channel_monitors, CHANNEL_MANAGER_PERSISTENCE_KEY,
CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE,
NETWORK_GRAPH_PERSISTENCE_KEY, NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE,
NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE, SCORER_PERSISTENCE_KEY,
SCORER_PERSISTENCE_PRIMARY_NAMESPACE, SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
};
use lightning::util::ser::ReadableArgs;
use lightning::util::sweep::OutputSweeper;
Expand Down Expand Up @@ -183,6 +189,7 @@ pub struct NodeBuilder {
gossip_source_config: Option<GossipSourceConfig>,
liquidity_source_config: Option<LiquiditySourceConfig>,
monitors_to_restore: Option<Vec<KeyValue>>,
reset_state: Option<ResetState>,
}

impl NodeBuilder {
Expand All @@ -199,13 +206,15 @@ impl NodeBuilder {
let gossip_source_config = None;
let liquidity_source_config = None;
let monitors_to_restore = None;
let reset_state = None;
Self {
config,
entropy_source_config,
chain_data_source_config,
gossip_source_config,
liquidity_source_config,
monitors_to_restore,
reset_state,
}
}

Expand All @@ -215,6 +224,12 @@ impl NodeBuilder {
self
}

/// Alby: persistent state components to reset on startup.
pub fn reset_state(&mut self, what: ResetState) -> &mut Self {
self.reset_state = Some(what);
self
}

/// Configures the [`Node`] instance to source its wallet entropy from a seed file on disk.
///
/// If the given file does not exist a new random seed file will be generated and
Expand Down Expand Up @@ -505,6 +520,7 @@ impl NodeBuilder {
seed_bytes,
logger,
Arc::new(vss_store),
self.reset_state,
)
}

Expand Down Expand Up @@ -537,6 +553,7 @@ impl NodeBuilder {
seed_bytes,
logger,
kv_store,
self.reset_state,
)
}
}
Expand Down Expand Up @@ -573,6 +590,11 @@ impl ArcedNodeBuilder {
self.inner.write().unwrap().restore_encoded_channel_monitors(monitors);
}

/// Alby: persistent state components to reset on startup.
pub fn reset_state(&self, what: ResetState) {
self.inner.write().unwrap().reset_state(what);
}

/// Configures the [`Node`] instance to source its wallet entropy from a seed file on disk.
///
/// If the given file does not exist a new random seed file will be generated and
Expand Down Expand Up @@ -772,8 +794,13 @@ fn build_with_store_internal(
config: Arc<Config>, chain_data_source_config: Option<&ChainDataSourceConfig>,
gossip_source_config: Option<&GossipSourceConfig>,
liquidity_source_config: Option<&LiquiditySourceConfig>, seed_bytes: [u8; 64],
logger: Arc<FilesystemLogger>, kv_store: Arc<DynStore>,
logger: Arc<FilesystemLogger>, kv_store: Arc<DynStore>, reset_state: Option<ResetState>,
) -> Result<Node, BuildError> {
// Alby: reset persistent state if requested.
if let Some(what) = reset_state {
reset_persistent_state(logger.clone(), kv_store.clone(), what);
}

// Initialize the status fields.
let is_listening = Arc::new(AtomicBool::new(false));
let node_metrics = match read_node_metrics(Arc::clone(&kv_store), Arc::clone(&logger)) {
Expand Down Expand Up @@ -1330,6 +1357,53 @@ fn derive_vss_xprv(
})
}

fn reset_persistent_state(
logger: Arc<FilesystemLogger>, kv_store: Arc<DynStore>, what: ResetState,
) {
let (node_metrics, scorer, network_graph) = match what {
ResetState::NodeMetrics => (true, false, false),
ResetState::Scorer => (false, true, false),
ResetState::NetworkGraph => (false, false, true),
ResetState::All => (true, true, true),
};

if node_metrics {
let result = kv_store.remove(
NODE_METRICS_PRIMARY_NAMESPACE,
NODE_METRICS_SECONDARY_NAMESPACE,
NODE_METRICS_KEY,
false,
);
if result.is_err() {
log_error!(logger, "Failed to reset node metrics: {}", result.unwrap_err());
}
}

if scorer {
let result = kv_store.remove(
SCORER_PERSISTENCE_PRIMARY_NAMESPACE,
SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
SCORER_PERSISTENCE_KEY,
false,
);
if result.is_err() {
log_error!(logger, "Failed to reset scorer: {}", result.unwrap_err());
}
}

if network_graph {
let result = kv_store.remove(
NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE,
NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE,
NETWORK_GRAPH_PERSISTENCE_KEY,
false,
);
if result.is_err() {
log_error!(logger, "Failed to reset network graph: {}", result.unwrap_err());
}
}
}

/// Sanitize the user-provided node alias to ensure that it is a valid protocol-specified UTF-8 string.
pub(crate) fn sanitize_alias(alias_str: &str) -> Result<NodeAlias, BuildError> {
let alias = alias_str.trim();
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ use payment::{
UnifiedQrPayment,
};
use peer_store::{PeerInfo, PeerStore};
#[cfg(feature = "uniffi")]
use types::ResetState;
use types::{
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph,
KeysManager, OnionMessenger, PeerManager, Router, Scorer, Sweeper, Wallet,
Expand Down
16 changes: 16 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,3 +399,19 @@ pub struct KeyValue {
/// Serialized value.
pub value: Vec<u8>,
}

/// State components to reset.
#[derive(Debug, Copy, Clone)]
pub enum ResetState {
/// Node metrics data (NODE_METRICS_KEY).
NodeMetrics,

/// Scorer data (SCORER_PERSISTENCE_KEY).
Scorer,

/// Network graph data (NETWORK_GRAPH_PERSISTENCE_KEY).
NetworkGraph,

/// All of the above.
All,
}

0 comments on commit 1c7508f

Please sign in to comment.