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
14 changes: 1 addition & 13 deletions lightning-block-sync/src/gossip.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,10 @@ use bitcoin::hash_types::BlockHash;
use bitcoin::transaction::{OutPoint, TxOut};

use lightning::ln::peer_handler::APeerManager;

use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};

use lightning::util::logger::Logger;
use lightning::util::native_async::FutureSpawner;

use std::collections::VecDeque;
use std::future::Future;
Expand DownExpand Up@@ -43,17 +42,6 @@ pub trait UtxoSource: BlockSource + 'static {
fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
}

/// A generic trait which is able to spawn futures in the background.
///
/// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
/// delegates to `tokio::spawn()`.
pub trait FutureSpawner: Send + Sync + 'static {
/// Spawns the given future as a background task.
///
/// This method MUST NOT block on the given future immediately.
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
}

#[cfg(feature = "tokio")]
/// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
pub struct TokioSpawner;
Expand Down
164 changes: 162 additions & 2 deletions lightning/src/chain/chainmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,12 +46,14 @@ use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHol
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{EntropySource, PeerStorageKey};
use crate::sign::{EntropySource, PeerStorageKey, SignerProvider};
use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use crate::types::features::{InitFeatures, NodeFeatures};
use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::errors::APIError;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::native_async::FutureSpawner;
use crate::util::persist::{KVStore, MonitorName, MonitorUpdatingPersisterAsync};
#[cfg(peer_storage)]
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
Expand DownExpand Up@@ -192,6 +194,17 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
/// the monitor already exists in the archive.
fn archive_persisted_channel(&self, monitor_name: MonitorName);

/// Fetches the set of [`ChannelMonitorUpdate`]s, previously persisted with
/// [`Self::update_persisted_channel`], which have completed.
///
/// Returning an update here is equivalent to calling
/// [`ChainMonitor::channel_monitor_updated`]. Because of this, this method is defaulted and
/// hidden in the docs.
#[doc(hidden)]
fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
Vec::new()
}
}

struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
Expand DownExpand Up@@ -235,6 +248,93 @@ impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, Chann
}
}

/// An unconstructable [`Persist`]er which is used under the hood when you call
/// [`ChainMonitor::new_async_beta`].
pub struct AsyncPersister<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, BI, FE>,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Deref for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Persist<<SP::Target as SignerProvider>::EcdsaSigner> for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
fn persist_new_channel(
&self, monitor_name: MonitorName,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_persist_new_channel(monitor_name, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn update_persisted_channel(
&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_update_persisted_channel(monitor_name, monitor_update, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn archive_persisted_channel(&self, monitor_name: MonitorName) {
self.persister.spawn_async_archive_persisted_channel(monitor_name);
}

fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
self.persister.get_and_clear_completed_updates()
}
}

/// An implementation of [`chain::Watch`] for monitoring channels.
///
/// Connected and disconnected blocks must be provided to `ChainMonitor` as documented by
Expand DownExpand Up@@ -291,6 +391,63 @@ pub struct ChainMonitor<
our_peerstorage_encryption_key: PeerStorageKey,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
SP: Deref + MaybeSend + MaybeSync + 'static,
C: Deref,
T: Deref + MaybeSend + MaybeSync + 'static,
F: Deref + MaybeSend + MaybeSync + 'static,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
>
ChainMonitor<
<SP::Target as SignerProvider>::EcdsaSigner,
C,
T,
F,
L,
AsyncPersister<K, S, L, ES, SP, T, F>,
ES,
> where
K::Target: KVStore + MaybeSync,
SP::Target: SignerProvider + Sized,
C::Target: chain::Filter,
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
ES::Target: EntropySource + Sized,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels.
///
/// This behaves the same as [`ChainMonitor::new`] except that it relies on
/// [`MonitorUpdatingPersisterAsync`] and thus allows persistence to be completed async.
///
/// Note that async monitor updating is considered beta, and bugs may be triggered by its use.
pub fn new_async_beta(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are now three ways to do persistence: sync, the previous async way via implementing a different Persist and this new_async_beta?

Is there any form of consolidation possible between the two async setups?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I mention it in the last commit, but I think the eventual consolidation should be that we merge MonitorUpdatingPersister into ChainMonitor and then the Persist interface is just the interface between ChannelManager and ChainMonitor, a user will always just instantiate a ChainMonitor with either a KVStore or a KVStoreSync and we'll deal with the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I just wondered if we should already now steer towards MonitorUpdatingPersister with an async kv store as the only way to do async. I don't think it is more "beta" than the current callback-based async?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd missed this. I don't really see a strong reason to change the current API and remove the manual-async approach immediately. Its not additional code to maintain (given the new logic uses it under the hood anyway) and we do have folks using it. That said, it does probably make sense to deprecate it, which I'll go ahead and do here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually nevermind, we should have a discussion about if we want to support async outside of rust, which would need the old API (or a way to make async KVStore work outside of rust, which I think we can do eventually as well).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider the bindings. Not great to remain stuck with multiple ways to do it, but not sure what we can do either.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can map the async stuff to bindings eventually, so its not like we're stuck, just a question of priorities.

chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not simply fee_estimator?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause I copied the one from new 🤷‍♂️

persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister: AsyncPersister { persister },
_entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
}

impl<
ChannelSigner: EcdsaChannelSigner,
C: Deref,
Expand DownExpand Up@@ -1357,6 +1514,9 @@ where
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
for (channel_id, update_id) in self.persister.get_and_clear_completed_updates() {
Comment thread
joostjager marked this conversation as resolved.
let _ = self.channel_monitor_updated(channel_id, update_id);
}
let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
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
14 changes: 1 addition & 13 deletions lightning-block-sync/src/gossip.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,10 @@ use bitcoin::hash_types::BlockHash;
use bitcoin::transaction::{OutPoint, TxOut};

use lightning::ln::peer_handler::APeerManager;

use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};

use lightning::util::logger::Logger;
use lightning::util::native_async::FutureSpawner;

use std::collections::VecDeque;
use std::future::Future;
Expand DownExpand Up@@ -43,17 +42,6 @@ pub trait UtxoSource: BlockSource + 'static {
fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
}

/// A generic trait which is able to spawn futures in the background.
///
/// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
/// delegates to `tokio::spawn()`.
pub trait FutureSpawner: Send + Sync + 'static {
/// Spawns the given future as a background task.
///
/// This method MUST NOT block on the given future immediately.
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
}

#[cfg(feature = "tokio")]
/// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
pub struct TokioSpawner;
Expand Down
164 changes: 162 additions & 2 deletions lightning/src/chain/chainmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,12 +46,14 @@ use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHol
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{EntropySource, PeerStorageKey};
use crate::sign::{EntropySource, PeerStorageKey, SignerProvider};
use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use crate::types::features::{InitFeatures, NodeFeatures};
use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::errors::APIError;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::native_async::FutureSpawner;
use crate::util::persist::{KVStore, MonitorName, MonitorUpdatingPersisterAsync};
#[cfg(peer_storage)]
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
Expand DownExpand Up@@ -192,6 +194,17 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
/// the monitor already exists in the archive.
fn archive_persisted_channel(&self, monitor_name: MonitorName);

/// Fetches the set of [`ChannelMonitorUpdate`]s, previously persisted with
/// [`Self::update_persisted_channel`], which have completed.
///
/// Returning an update here is equivalent to calling
/// [`ChainMonitor::channel_monitor_updated`]. Because of this, this method is defaulted and
/// hidden in the docs.
#[doc(hidden)]
fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
Vec::new()
}
}

struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
Expand DownExpand Up@@ -235,6 +248,93 @@ impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, Chann
}
}

/// An unconstructable [`Persist`]er which is used under the hood when you call
/// [`ChainMonitor::new_async_beta`].
pub struct AsyncPersister<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, BI, FE>,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Deref for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Persist<<SP::Target as SignerProvider>::EcdsaSigner> for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
fn persist_new_channel(
&self, monitor_name: MonitorName,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_persist_new_channel(monitor_name, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn update_persisted_channel(
&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_update_persisted_channel(monitor_name, monitor_update, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn archive_persisted_channel(&self, monitor_name: MonitorName) {
self.persister.spawn_async_archive_persisted_channel(monitor_name);
}

fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
self.persister.get_and_clear_completed_updates()
}
}

/// An implementation of [`chain::Watch`] for monitoring channels.
///
/// Connected and disconnected blocks must be provided to `ChainMonitor` as documented by
Expand DownExpand Up@@ -291,6 +391,63 @@ pub struct ChainMonitor<
our_peerstorage_encryption_key: PeerStorageKey,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
SP: Deref + MaybeSend + MaybeSync + 'static,
C: Deref,
T: Deref + MaybeSend + MaybeSync + 'static,
F: Deref + MaybeSend + MaybeSync + 'static,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
>
ChainMonitor<
<SP::Target as SignerProvider>::EcdsaSigner,
C,
T,
F,
L,
AsyncPersister<K, S, L, ES, SP, T, F>,
ES,
> where
K::Target: KVStore + MaybeSync,
SP::Target: SignerProvider + Sized,
C::Target: chain::Filter,
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
ES::Target: EntropySource + Sized,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels.
///
/// This behaves the same as [`ChainMonitor::new`] except that it relies on
/// [`MonitorUpdatingPersisterAsync`] and thus allows persistence to be completed async.
///
/// Note that async monitor updating is considered beta, and bugs may be triggered by its use.
pub fn new_async_beta(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are now three ways to do persistence: sync, the previous async way via implementing a different Persist and this new_async_beta?

Is there any form of consolidation possible between the two async setups?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I mention it in the last commit, but I think the eventual consolidation should be that we merge MonitorUpdatingPersister into ChainMonitor and then the Persist interface is just the interface between ChannelManager and ChainMonitor, a user will always just instantiate a ChainMonitor with either a KVStore or a KVStoreSync and we'll deal with the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I just wondered if we should already now steer towards MonitorUpdatingPersister with an async kv store as the only way to do async. I don't think it is more "beta" than the current callback-based async?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd missed this. I don't really see a strong reason to change the current API and remove the manual-async approach immediately. Its not additional code to maintain (given the new logic uses it under the hood anyway) and we do have folks using it. That said, it does probably make sense to deprecate it, which I'll go ahead and do here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually nevermind, we should have a discussion about if we want to support async outside of rust, which would need the old API (or a way to make async KVStore work outside of rust, which I think we can do eventually as well).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider the bindings. Not great to remain stuck with multiple ways to do it, but not sure what we can do either.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can map the async stuff to bindings eventually, so its not like we're stuck, just a question of priorities.

chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not simply fee_estimator?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause I copied the one from new 🤷‍♂️

persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister: AsyncPersister { persister },
_entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
}

impl<
ChannelSigner: EcdsaChannelSigner,
C: Deref,
Expand DownExpand Up@@ -1357,6 +1514,9 @@ where
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
for (channel_id, update_id) in self.persister.get_and_clear_completed_updates() {
Comment thread
joostjager marked this conversation as resolved.
let _ = self.channel_monitor_updated(channel_id, update_id);
}
let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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
14 changes: 1 addition & 13 deletions lightning-block-sync/src/gossip.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,10 @@ use bitcoin::hash_types::BlockHash;
use bitcoin::transaction::{OutPoint, TxOut};

use lightning::ln::peer_handler::APeerManager;

use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};

use lightning::util::logger::Logger;
use lightning::util::native_async::FutureSpawner;

use std::collections::VecDeque;
use std::future::Future;
Expand DownExpand Up@@ -43,17 +42,6 @@ pub trait UtxoSource: BlockSource + 'static {
fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
}

/// A generic trait which is able to spawn futures in the background.
///
/// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
/// delegates to `tokio::spawn()`.
pub trait FutureSpawner: Send + Sync + 'static {
/// Spawns the given future as a background task.
///
/// This method MUST NOT block on the given future immediately.
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
}

#[cfg(feature = "tokio")]
/// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
pub struct TokioSpawner;
Expand Down
164 changes: 162 additions & 2 deletions lightning/src/chain/chainmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,12 +46,14 @@ use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHol
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{EntropySource, PeerStorageKey};
use crate::sign::{EntropySource, PeerStorageKey, SignerProvider};
use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use crate::types::features::{InitFeatures, NodeFeatures};
use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::errors::APIError;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::native_async::FutureSpawner;
use crate::util::persist::{KVStore, MonitorName, MonitorUpdatingPersisterAsync};
#[cfg(peer_storage)]
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
Expand DownExpand Up@@ -192,6 +194,17 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
/// the monitor already exists in the archive.
fn archive_persisted_channel(&self, monitor_name: MonitorName);

/// Fetches the set of [`ChannelMonitorUpdate`]s, previously persisted with
/// [`Self::update_persisted_channel`], which have completed.
///
/// Returning an update here is equivalent to calling
/// [`ChainMonitor::channel_monitor_updated`]. Because of this, this method is defaulted and
/// hidden in the docs.
#[doc(hidden)]
fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
Vec::new()
}
}

struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
Expand DownExpand Up@@ -235,6 +248,93 @@ impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, Chann
}
}

/// An unconstructable [`Persist`]er which is used under the hood when you call
/// [`ChainMonitor::new_async_beta`].
pub struct AsyncPersister<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, BI, FE>,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Deref for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Persist<<SP::Target as SignerProvider>::EcdsaSigner> for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
fn persist_new_channel(
&self, monitor_name: MonitorName,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_persist_new_channel(monitor_name, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn update_persisted_channel(
&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_update_persisted_channel(monitor_name, monitor_update, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn archive_persisted_channel(&self, monitor_name: MonitorName) {
self.persister.spawn_async_archive_persisted_channel(monitor_name);
}

fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
self.persister.get_and_clear_completed_updates()
}
}

/// An implementation of [`chain::Watch`] for monitoring channels.
///
/// Connected and disconnected blocks must be provided to `ChainMonitor` as documented by
Expand DownExpand Up@@ -291,6 +391,63 @@ pub struct ChainMonitor<
our_peerstorage_encryption_key: PeerStorageKey,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
SP: Deref + MaybeSend + MaybeSync + 'static,
C: Deref,
T: Deref + MaybeSend + MaybeSync + 'static,
F: Deref + MaybeSend + MaybeSync + 'static,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
>
ChainMonitor<
<SP::Target as SignerProvider>::EcdsaSigner,
C,
T,
F,
L,
AsyncPersister<K, S, L, ES, SP, T, F>,
ES,
> where
K::Target: KVStore + MaybeSync,
SP::Target: SignerProvider + Sized,
C::Target: chain::Filter,
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
ES::Target: EntropySource + Sized,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels.
///
/// This behaves the same as [`ChainMonitor::new`] except that it relies on
/// [`MonitorUpdatingPersisterAsync`] and thus allows persistence to be completed async.
///
/// Note that async monitor updating is considered beta, and bugs may be triggered by its use.
pub fn new_async_beta(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are now three ways to do persistence: sync, the previous async way via implementing a different Persist and this new_async_beta?

Is there any form of consolidation possible between the two async setups?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I mention it in the last commit, but I think the eventual consolidation should be that we merge MonitorUpdatingPersister into ChainMonitor and then the Persist interface is just the interface between ChannelManager and ChainMonitor, a user will always just instantiate a ChainMonitor with either a KVStore or a KVStoreSync and we'll deal with the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I just wondered if we should already now steer towards MonitorUpdatingPersister with an async kv store as the only way to do async. I don't think it is more "beta" than the current callback-based async?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd missed this. I don't really see a strong reason to change the current API and remove the manual-async approach immediately. Its not additional code to maintain (given the new logic uses it under the hood anyway) and we do have folks using it. That said, it does probably make sense to deprecate it, which I'll go ahead and do here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually nevermind, we should have a discussion about if we want to support async outside of rust, which would need the old API (or a way to make async KVStore work outside of rust, which I think we can do eventually as well).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider the bindings. Not great to remain stuck with multiple ways to do it, but not sure what we can do either.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can map the async stuff to bindings eventually, so its not like we're stuck, just a question of priorities.

chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not simply fee_estimator?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause I copied the one from new 🤷‍♂️

persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister: AsyncPersister { persister },
_entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
}

impl<
ChannelSigner: EcdsaChannelSigner,
C: Deref,
Expand DownExpand Up@@ -1357,6 +1514,9 @@ where
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
for (channel_id, update_id) in self.persister.get_and_clear_completed_updates() {
Comment thread
joostjager marked this conversation as resolved.
let _ = self.channel_monitor_updated(channel_id, update_id);
}
let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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
14 changes: 1 addition & 13 deletions lightning-block-sync/src/gossip.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,10 @@ use bitcoin::hash_types::BlockHash;
use bitcoin::transaction::{OutPoint, TxOut};

use lightning::ln::peer_handler::APeerManager;

use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};

use lightning::util::logger::Logger;
use lightning::util::native_async::FutureSpawner;

use std::collections::VecDeque;
use std::future::Future;
Expand DownExpand Up@@ -43,17 +42,6 @@ pub trait UtxoSource: BlockSource + 'static {
fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
}

/// A generic trait which is able to spawn futures in the background.
///
/// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
/// delegates to `tokio::spawn()`.
pub trait FutureSpawner: Send + Sync + 'static {
/// Spawns the given future as a background task.
///
/// This method MUST NOT block on the given future immediately.
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
}

#[cfg(feature = "tokio")]
/// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
pub struct TokioSpawner;
Expand Down
164 changes: 162 additions & 2 deletions lightning/src/chain/chainmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,12 +46,14 @@ use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHol
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{EntropySource, PeerStorageKey};
use crate::sign::{EntropySource, PeerStorageKey, SignerProvider};
use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use crate::types::features::{InitFeatures, NodeFeatures};
use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::errors::APIError;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::native_async::FutureSpawner;
use crate::util::persist::{KVStore, MonitorName, MonitorUpdatingPersisterAsync};
#[cfg(peer_storage)]
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
Expand DownExpand Up@@ -192,6 +194,17 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
/// the monitor already exists in the archive.
fn archive_persisted_channel(&self, monitor_name: MonitorName);

/// Fetches the set of [`ChannelMonitorUpdate`]s, previously persisted with
/// [`Self::update_persisted_channel`], which have completed.
///
/// Returning an update here is equivalent to calling
/// [`ChainMonitor::channel_monitor_updated`]. Because of this, this method is defaulted and
/// hidden in the docs.
#[doc(hidden)]
fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
Vec::new()
}
}

struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
Expand DownExpand Up@@ -235,6 +248,93 @@ impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, Chann
}
}

/// An unconstructable [`Persist`]er which is used under the hood when you call
/// [`ChainMonitor::new_async_beta`].
pub struct AsyncPersister<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, BI, FE>,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Deref for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Persist<<SP::Target as SignerProvider>::EcdsaSigner> for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
fn persist_new_channel(
&self, monitor_name: MonitorName,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_persist_new_channel(monitor_name, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn update_persisted_channel(
&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_update_persisted_channel(monitor_name, monitor_update, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn archive_persisted_channel(&self, monitor_name: MonitorName) {
self.persister.spawn_async_archive_persisted_channel(monitor_name);
}

fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
self.persister.get_and_clear_completed_updates()
}
}

/// An implementation of [`chain::Watch`] for monitoring channels.
///
/// Connected and disconnected blocks must be provided to `ChainMonitor` as documented by
Expand DownExpand Up@@ -291,6 +391,63 @@ pub struct ChainMonitor<
our_peerstorage_encryption_key: PeerStorageKey,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
SP: Deref + MaybeSend + MaybeSync + 'static,
C: Deref,
T: Deref + MaybeSend + MaybeSync + 'static,
F: Deref + MaybeSend + MaybeSync + 'static,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
>
ChainMonitor<
<SP::Target as SignerProvider>::EcdsaSigner,
C,
T,
F,
L,
AsyncPersister<K, S, L, ES, SP, T, F>,
ES,
> where
K::Target: KVStore + MaybeSync,
SP::Target: SignerProvider + Sized,
C::Target: chain::Filter,
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
ES::Target: EntropySource + Sized,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels.
///
/// This behaves the same as [`ChainMonitor::new`] except that it relies on
/// [`MonitorUpdatingPersisterAsync`] and thus allows persistence to be completed async.
///
/// Note that async monitor updating is considered beta, and bugs may be triggered by its use.
pub fn new_async_beta(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are now three ways to do persistence: sync, the previous async way via implementing a different Persist and this new_async_beta?

Is there any form of consolidation possible between the two async setups?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I mention it in the last commit, but I think the eventual consolidation should be that we merge MonitorUpdatingPersister into ChainMonitor and then the Persist interface is just the interface between ChannelManager and ChainMonitor, a user will always just instantiate a ChainMonitor with either a KVStore or a KVStoreSync and we'll deal with the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I just wondered if we should already now steer towards MonitorUpdatingPersister with an async kv store as the only way to do async. I don't think it is more "beta" than the current callback-based async?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd missed this. I don't really see a strong reason to change the current API and remove the manual-async approach immediately. Its not additional code to maintain (given the new logic uses it under the hood anyway) and we do have folks using it. That said, it does probably make sense to deprecate it, which I'll go ahead and do here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually nevermind, we should have a discussion about if we want to support async outside of rust, which would need the old API (or a way to make async KVStore work outside of rust, which I think we can do eventually as well).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider the bindings. Not great to remain stuck with multiple ways to do it, but not sure what we can do either.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can map the async stuff to bindings eventually, so its not like we're stuck, just a question of priorities.

chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not simply fee_estimator?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause I copied the one from new 🤷‍♂️

persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister: AsyncPersister { persister },
_entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
}

impl<
ChannelSigner: EcdsaChannelSigner,
C: Deref,
Expand DownExpand Up@@ -1357,6 +1514,9 @@ where
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
for (channel_id, update_id) in self.persister.get_and_clear_completed_updates() {
Comment thread
joostjager marked this conversation as resolved.
let _ = self.channel_monitor_updated(channel_id, update_id);
}
let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
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
14 changes: 1 addition & 13 deletions lightning-block-sync/src/gossip.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,10 @@ use bitcoin::hash_types::BlockHash;
use bitcoin::transaction::{OutPoint, TxOut};

use lightning::ln::peer_handler::APeerManager;

use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};

use lightning::util::logger::Logger;
use lightning::util::native_async::FutureSpawner;

use std::collections::VecDeque;
use std::future::Future;
Expand DownExpand Up@@ -43,17 +42,6 @@ pub trait UtxoSource: BlockSource + 'static {
fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
}

/// A generic trait which is able to spawn futures in the background.
///
/// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
/// delegates to `tokio::spawn()`.
pub trait FutureSpawner: Send + Sync + 'static {
/// Spawns the given future as a background task.
///
/// This method MUST NOT block on the given future immediately.
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
}

#[cfg(feature = "tokio")]
/// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
pub struct TokioSpawner;
Expand Down
164 changes: 162 additions & 2 deletions lightning/src/chain/chainmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,12 +46,14 @@ use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHol
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{EntropySource, PeerStorageKey};
use crate::sign::{EntropySource, PeerStorageKey, SignerProvider};
use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use crate::types::features::{InitFeatures, NodeFeatures};
use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::errors::APIError;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::native_async::FutureSpawner;
use crate::util::persist::{KVStore, MonitorName, MonitorUpdatingPersisterAsync};
#[cfg(peer_storage)]
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
Expand DownExpand Up@@ -192,6 +194,17 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
/// the monitor already exists in the archive.
fn archive_persisted_channel(&self, monitor_name: MonitorName);

/// Fetches the set of [`ChannelMonitorUpdate`]s, previously persisted with
/// [`Self::update_persisted_channel`], which have completed.
///
/// Returning an update here is equivalent to calling
/// [`ChainMonitor::channel_monitor_updated`]. Because of this, this method is defaulted and
/// hidden in the docs.
#[doc(hidden)]
fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
Vec::new()
}
}

struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
Expand DownExpand Up@@ -235,6 +248,93 @@ impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, Chann
}
}

/// An unconstructable [`Persist`]er which is used under the hood when you call
/// [`ChainMonitor::new_async_beta`].
pub struct AsyncPersister<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, BI, FE>,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Deref for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Persist<<SP::Target as SignerProvider>::EcdsaSigner> for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
fn persist_new_channel(
&self, monitor_name: MonitorName,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_persist_new_channel(monitor_name, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn update_persisted_channel(
&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_update_persisted_channel(monitor_name, monitor_update, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn archive_persisted_channel(&self, monitor_name: MonitorName) {
self.persister.spawn_async_archive_persisted_channel(monitor_name);
}

fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
self.persister.get_and_clear_completed_updates()
}
}

/// An implementation of [`chain::Watch`] for monitoring channels.
///
/// Connected and disconnected blocks must be provided to `ChainMonitor` as documented by
Expand DownExpand Up@@ -291,6 +391,63 @@ pub struct ChainMonitor<
our_peerstorage_encryption_key: PeerStorageKey,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
SP: Deref + MaybeSend + MaybeSync + 'static,
C: Deref,
T: Deref + MaybeSend + MaybeSync + 'static,
F: Deref + MaybeSend + MaybeSync + 'static,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
>
ChainMonitor<
<SP::Target as SignerProvider>::EcdsaSigner,
C,
T,
F,
L,
AsyncPersister<K, S, L, ES, SP, T, F>,
ES,
> where
K::Target: KVStore + MaybeSync,
SP::Target: SignerProvider + Sized,
C::Target: chain::Filter,
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
ES::Target: EntropySource + Sized,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels.
///
/// This behaves the same as [`ChainMonitor::new`] except that it relies on
/// [`MonitorUpdatingPersisterAsync`] and thus allows persistence to be completed async.
///
/// Note that async monitor updating is considered beta, and bugs may be triggered by its use.
pub fn new_async_beta(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are now three ways to do persistence: sync, the previous async way via implementing a different Persist and this new_async_beta?

Is there any form of consolidation possible between the two async setups?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I mention it in the last commit, but I think the eventual consolidation should be that we merge MonitorUpdatingPersister into ChainMonitor and then the Persist interface is just the interface between ChannelManager and ChainMonitor, a user will always just instantiate a ChainMonitor with either a KVStore or a KVStoreSync and we'll deal with the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I just wondered if we should already now steer towards MonitorUpdatingPersister with an async kv store as the only way to do async. I don't think it is more "beta" than the current callback-based async?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd missed this. I don't really see a strong reason to change the current API and remove the manual-async approach immediately. Its not additional code to maintain (given the new logic uses it under the hood anyway) and we do have folks using it. That said, it does probably make sense to deprecate it, which I'll go ahead and do here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually nevermind, we should have a discussion about if we want to support async outside of rust, which would need the old API (or a way to make async KVStore work outside of rust, which I think we can do eventually as well).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider the bindings. Not great to remain stuck with multiple ways to do it, but not sure what we can do either.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can map the async stuff to bindings eventually, so its not like we're stuck, just a question of priorities.

chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not simply fee_estimator?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause I copied the one from new 🤷‍♂️

persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister: AsyncPersister { persister },
_entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
}

impl<
ChannelSigner: EcdsaChannelSigner,
C: Deref,
Expand DownExpand Up@@ -1357,6 +1514,9 @@ where
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
for (channel_id, update_id) in self.persister.get_and_clear_completed_updates() {
Comment thread
joostjager marked this conversation as resolved.
let _ = self.channel_monitor_updated(channel_id, update_id);
}
let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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
14 changes: 1 addition & 13 deletions lightning-block-sync/src/gossip.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,10 @@ use bitcoin::hash_types::BlockHash;
use bitcoin::transaction::{OutPoint, TxOut};

use lightning::ln::peer_handler::APeerManager;

use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};

use lightning::util::logger::Logger;
use lightning::util::native_async::FutureSpawner;

use std::collections::VecDeque;
use std::future::Future;
Expand DownExpand Up@@ -43,17 +42,6 @@ pub trait UtxoSource: BlockSource + 'static {
fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
}

/// A generic trait which is able to spawn futures in the background.
///
/// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
/// delegates to `tokio::spawn()`.
pub trait FutureSpawner: Send + Sync + 'static {
/// Spawns the given future as a background task.
///
/// This method MUST NOT block on the given future immediately.
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
}

#[cfg(feature = "tokio")]
/// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
pub struct TokioSpawner;
Expand Down
164 changes: 162 additions & 2 deletions lightning/src/chain/chainmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,12 +46,14 @@ use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHol
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{EntropySource, PeerStorageKey};
use crate::sign::{EntropySource, PeerStorageKey, SignerProvider};
use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use crate::types::features::{InitFeatures, NodeFeatures};
use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::errors::APIError;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::native_async::FutureSpawner;
use crate::util::persist::{KVStore, MonitorName, MonitorUpdatingPersisterAsync};
#[cfg(peer_storage)]
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
Expand DownExpand Up@@ -192,6 +194,17 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
/// the monitor already exists in the archive.
fn archive_persisted_channel(&self, monitor_name: MonitorName);

/// Fetches the set of [`ChannelMonitorUpdate`]s, previously persisted with
/// [`Self::update_persisted_channel`], which have completed.
///
/// Returning an update here is equivalent to calling
/// [`ChainMonitor::channel_monitor_updated`]. Because of this, this method is defaulted and
/// hidden in the docs.
#[doc(hidden)]
fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
Vec::new()
}
}

struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
Expand DownExpand Up@@ -235,6 +248,93 @@ impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, Chann
}
}

/// An unconstructable [`Persist`]er which is used under the hood when you call
/// [`ChainMonitor::new_async_beta`].
pub struct AsyncPersister<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, BI, FE>,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Deref for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Persist<<SP::Target as SignerProvider>::EcdsaSigner> for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
fn persist_new_channel(
&self, monitor_name: MonitorName,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_persist_new_channel(monitor_name, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn update_persisted_channel(
&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_update_persisted_channel(monitor_name, monitor_update, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn archive_persisted_channel(&self, monitor_name: MonitorName) {
self.persister.spawn_async_archive_persisted_channel(monitor_name);
}

fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
self.persister.get_and_clear_completed_updates()
}
}

/// An implementation of [`chain::Watch`] for monitoring channels.
///
/// Connected and disconnected blocks must be provided to `ChainMonitor` as documented by
Expand DownExpand Up@@ -291,6 +391,63 @@ pub struct ChainMonitor<
our_peerstorage_encryption_key: PeerStorageKey,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
SP: Deref + MaybeSend + MaybeSync + 'static,
C: Deref,
T: Deref + MaybeSend + MaybeSync + 'static,
F: Deref + MaybeSend + MaybeSync + 'static,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
>
ChainMonitor<
<SP::Target as SignerProvider>::EcdsaSigner,
C,
T,
F,
L,
AsyncPersister<K, S, L, ES, SP, T, F>,
ES,
> where
K::Target: KVStore + MaybeSync,
SP::Target: SignerProvider + Sized,
C::Target: chain::Filter,
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
ES::Target: EntropySource + Sized,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels.
///
/// This behaves the same as [`ChainMonitor::new`] except that it relies on
/// [`MonitorUpdatingPersisterAsync`] and thus allows persistence to be completed async.
///
/// Note that async monitor updating is considered beta, and bugs may be triggered by its use.
pub fn new_async_beta(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are now three ways to do persistence: sync, the previous async way via implementing a different Persist and this new_async_beta?

Is there any form of consolidation possible between the two async setups?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I mention it in the last commit, but I think the eventual consolidation should be that we merge MonitorUpdatingPersister into ChainMonitor and then the Persist interface is just the interface between ChannelManager and ChainMonitor, a user will always just instantiate a ChainMonitor with either a KVStore or a KVStoreSync and we'll deal with the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I just wondered if we should already now steer towards MonitorUpdatingPersister with an async kv store as the only way to do async. I don't think it is more "beta" than the current callback-based async?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd missed this. I don't really see a strong reason to change the current API and remove the manual-async approach immediately. Its not additional code to maintain (given the new logic uses it under the hood anyway) and we do have folks using it. That said, it does probably make sense to deprecate it, which I'll go ahead and do here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually nevermind, we should have a discussion about if we want to support async outside of rust, which would need the old API (or a way to make async KVStore work outside of rust, which I think we can do eventually as well).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider the bindings. Not great to remain stuck with multiple ways to do it, but not sure what we can do either.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can map the async stuff to bindings eventually, so its not like we're stuck, just a question of priorities.

chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not simply fee_estimator?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause I copied the one from new 🤷‍♂️

persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister: AsyncPersister { persister },
_entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
}

impl<
ChannelSigner: EcdsaChannelSigner,
C: Deref,
Expand DownExpand Up@@ -1357,6 +1514,9 @@ where
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
for (channel_id, update_id) in self.persister.get_and_clear_completed_updates() {
Comment thread
joostjager marked this conversation as resolved.
let _ = self.channel_monitor_updated(channel_id, update_id);
}
let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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
14 changes: 1 addition & 13 deletions lightning-block-sync/src/gossip.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,10 @@ use bitcoin::hash_types::BlockHash;
use bitcoin::transaction::{OutPoint, TxOut};

use lightning::ln::peer_handler::APeerManager;

use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};

use lightning::util::logger::Logger;
use lightning::util::native_async::FutureSpawner;

use std::collections::VecDeque;
use std::future::Future;
Expand DownExpand Up@@ -43,17 +42,6 @@ pub trait UtxoSource: BlockSource + 'static {
fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
}

/// A generic trait which is able to spawn futures in the background.
///
/// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
/// delegates to `tokio::spawn()`.
pub trait FutureSpawner: Send + Sync + 'static {
/// Spawns the given future as a background task.
///
/// This method MUST NOT block on the given future immediately.
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
}

#[cfg(feature = "tokio")]
/// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
pub struct TokioSpawner;
Expand Down
164 changes: 162 additions & 2 deletions lightning/src/chain/chainmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,12 +46,14 @@ use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHol
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{EntropySource, PeerStorageKey};
use crate::sign::{EntropySource, PeerStorageKey, SignerProvider};
use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use crate::types::features::{InitFeatures, NodeFeatures};
use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::errors::APIError;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::native_async::FutureSpawner;
use crate::util::persist::{KVStore, MonitorName, MonitorUpdatingPersisterAsync};
#[cfg(peer_storage)]
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
Expand DownExpand Up@@ -192,6 +194,17 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
/// the monitor already exists in the archive.
fn archive_persisted_channel(&self, monitor_name: MonitorName);

/// Fetches the set of [`ChannelMonitorUpdate`]s, previously persisted with
/// [`Self::update_persisted_channel`], which have completed.
///
/// Returning an update here is equivalent to calling
/// [`ChainMonitor::channel_monitor_updated`]. Because of this, this method is defaulted and
/// hidden in the docs.
#[doc(hidden)]
fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
Vec::new()
}
}

struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
Expand DownExpand Up@@ -235,6 +248,93 @@ impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, Chann
}
}

/// An unconstructable [`Persist`]er which is used under the hood when you call
/// [`ChainMonitor::new_async_beta`].
pub struct AsyncPersister<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, BI, FE>,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Deref for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Persist<<SP::Target as SignerProvider>::EcdsaSigner> for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
fn persist_new_channel(
&self, monitor_name: MonitorName,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_persist_new_channel(monitor_name, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn update_persisted_channel(
&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_update_persisted_channel(monitor_name, monitor_update, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn archive_persisted_channel(&self, monitor_name: MonitorName) {
self.persister.spawn_async_archive_persisted_channel(monitor_name);
}

fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
self.persister.get_and_clear_completed_updates()
}
}

/// An implementation of [`chain::Watch`] for monitoring channels.
///
/// Connected and disconnected blocks must be provided to `ChainMonitor` as documented by
Expand DownExpand Up@@ -291,6 +391,63 @@ pub struct ChainMonitor<
our_peerstorage_encryption_key: PeerStorageKey,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
SP: Deref + MaybeSend + MaybeSync + 'static,
C: Deref,
T: Deref + MaybeSend + MaybeSync + 'static,
F: Deref + MaybeSend + MaybeSync + 'static,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
>
ChainMonitor<
<SP::Target as SignerProvider>::EcdsaSigner,
C,
T,
F,
L,
AsyncPersister<K, S, L, ES, SP, T, F>,
ES,
> where
K::Target: KVStore + MaybeSync,
SP::Target: SignerProvider + Sized,
C::Target: chain::Filter,
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
ES::Target: EntropySource + Sized,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels.
///
/// This behaves the same as [`ChainMonitor::new`] except that it relies on
/// [`MonitorUpdatingPersisterAsync`] and thus allows persistence to be completed async.
///
/// Note that async monitor updating is considered beta, and bugs may be triggered by its use.
pub fn new_async_beta(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are now three ways to do persistence: sync, the previous async way via implementing a different Persist and this new_async_beta?

Is there any form of consolidation possible between the two async setups?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I mention it in the last commit, but I think the eventual consolidation should be that we merge MonitorUpdatingPersister into ChainMonitor and then the Persist interface is just the interface between ChannelManager and ChainMonitor, a user will always just instantiate a ChainMonitor with either a KVStore or a KVStoreSync and we'll deal with the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I just wondered if we should already now steer towards MonitorUpdatingPersister with an async kv store as the only way to do async. I don't think it is more "beta" than the current callback-based async?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd missed this. I don't really see a strong reason to change the current API and remove the manual-async approach immediately. Its not additional code to maintain (given the new logic uses it under the hood anyway) and we do have folks using it. That said, it does probably make sense to deprecate it, which I'll go ahead and do here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually nevermind, we should have a discussion about if we want to support async outside of rust, which would need the old API (or a way to make async KVStore work outside of rust, which I think we can do eventually as well).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider the bindings. Not great to remain stuck with multiple ways to do it, but not sure what we can do either.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can map the async stuff to bindings eventually, so its not like we're stuck, just a question of priorities.

chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not simply fee_estimator?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause I copied the one from new 🤷‍♂️

persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister: AsyncPersister { persister },
_entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
}

impl<
ChannelSigner: EcdsaChannelSigner,
C: Deref,
Expand DownExpand Up@@ -1357,6 +1514,9 @@ where
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
for (channel_id, update_id) in self.persister.get_and_clear_completed_updates() {
Comment thread
joostjager marked this conversation as resolved.
let _ = self.channel_monitor_updated(channel_id, update_id);
}
let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
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
14 changes: 1 addition & 13 deletions lightning-block-sync/src/gossip.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,10 @@ use bitcoin::hash_types::BlockHash;
use bitcoin::transaction::{OutPoint, TxOut};

use lightning::ln::peer_handler::APeerManager;

use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoLookupError, UtxoResult};

use lightning::util::logger::Logger;
use lightning::util::native_async::FutureSpawner;

use std::collections::VecDeque;
use std::future::Future;
Expand DownExpand Up@@ -43,17 +42,6 @@ pub trait UtxoSource: BlockSource + 'static {
fn is_output_unspent<'a>(&'a self, outpoint: OutPoint) -> AsyncBlockSourceResult<'a, bool>;
}

/// A generic trait which is able to spawn futures in the background.
///
/// If the `tokio` feature is enabled, this is implemented on `TokioSpawner` struct which
/// delegates to `tokio::spawn()`.
pub trait FutureSpawner: Send + Sync + 'static {
/// Spawns the given future as a background task.
///
/// This method MUST NOT block on the given future immediately.
fn spawn<T: Future<Output = ()> + Send + 'static>(&self, future: T);
}

#[cfg(feature = "tokio")]
/// A trivial [`FutureSpawner`] which delegates to `tokio::spawn`.
pub struct TokioSpawner;
Expand Down
164 changes: 162 additions & 2 deletions lightning/src/chain/chainmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,12 +46,14 @@ use crate::ln::our_peer_storage::{DecryptedOurPeerStorage, PeerStorageMonitorHol
use crate::ln::types::ChannelId;
use crate::prelude::*;
use crate::sign::ecdsa::EcdsaChannelSigner;
use crate::sign::{EntropySource, PeerStorageKey};
use crate::sign::{EntropySource, PeerStorageKey, SignerProvider};
use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard};
use crate::types::features::{InitFeatures, NodeFeatures};
use crate::util::async_poll::{MaybeSend, MaybeSync};
use crate::util::errors::APIError;
use crate::util::logger::{Logger, WithContext};
use crate::util::persist::MonitorName;
use crate::util::native_async::FutureSpawner;
use crate::util::persist::{KVStore, MonitorName, MonitorUpdatingPersisterAsync};
#[cfg(peer_storage)]
use crate::util::ser::{VecWriter, Writeable};
use crate::util::wakers::{Future, Notifier};
Expand DownExpand Up@@ -192,6 +194,17 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
/// the monitor already exists in the archive.
fn archive_persisted_channel(&self, monitor_name: MonitorName);

/// Fetches the set of [`ChannelMonitorUpdate`]s, previously persisted with
/// [`Self::update_persisted_channel`], which have completed.
///
/// Returning an update here is equivalent to calling
/// [`ChainMonitor::channel_monitor_updated`]. Because of this, this method is defaulted and
/// hidden in the docs.
#[doc(hidden)]
fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
Vec::new()
}
}

struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
Expand DownExpand Up@@ -235,6 +248,93 @@ impl<ChannelSigner: EcdsaChannelSigner> Deref for LockedChannelMonitor<'_, Chann
}
}

/// An unconstructable [`Persist`]er which is used under the hood when you call
/// [`ChainMonitor::new_async_beta`].
pub struct AsyncPersister<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, BI, FE>,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Deref for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
{
type Target = Self;
fn deref(&self) -> &Self {
self
}
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
SP: Deref + MaybeSend + MaybeSync + 'static,
BI: Deref + MaybeSend + MaybeSync + 'static,
FE: Deref + MaybeSend + MaybeSync + 'static,
> Persist<<SP::Target as SignerProvider>::EcdsaSigner> for AsyncPersister<K, S, L, ES, SP, BI, FE>
where
K::Target: KVStore + MaybeSync,
L::Target: Logger,
ES::Target: EntropySource + Sized,
SP::Target: SignerProvider + Sized,
BI::Target: BroadcasterInterface,
FE::Target: FeeEstimator,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
fn persist_new_channel(
&self, monitor_name: MonitorName,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_persist_new_channel(monitor_name, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn update_persisted_channel(
&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>,
monitor: &ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>,
) -> ChannelMonitorUpdateStatus {
self.persister.spawn_async_update_persisted_channel(monitor_name, monitor_update, monitor);
ChannelMonitorUpdateStatus::InProgress
}

fn archive_persisted_channel(&self, monitor_name: MonitorName) {
self.persister.spawn_async_archive_persisted_channel(monitor_name);
}

fn get_and_clear_completed_updates(&self) -> Vec<(ChannelId, u64)> {
self.persister.get_and_clear_completed_updates()
}
}

/// An implementation of [`chain::Watch`] for monitoring channels.
///
/// Connected and disconnected blocks must be provided to `ChainMonitor` as documented by
Expand DownExpand Up@@ -291,6 +391,63 @@ pub struct ChainMonitor<
our_peerstorage_encryption_key: PeerStorageKey,
}

impl<
K: Deref + MaybeSend + MaybeSync + 'static,
S: FutureSpawner,
SP: Deref + MaybeSend + MaybeSync + 'static,
C: Deref,
T: Deref + MaybeSend + MaybeSync + 'static,
F: Deref + MaybeSend + MaybeSync + 'static,
L: Deref + MaybeSend + MaybeSync + 'static,
ES: Deref + MaybeSend + MaybeSync + 'static,
>
ChainMonitor<
<SP::Target as SignerProvider>::EcdsaSigner,
C,
T,
F,
L,
AsyncPersister<K, S, L, ES, SP, T, F>,
ES,
> where
K::Target: KVStore + MaybeSync,
SP::Target: SignerProvider + Sized,
C::Target: chain::Filter,
T::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
ES::Target: EntropySource + Sized,
<SP::Target as SignerProvider>::EcdsaSigner: MaybeSend + 'static,
{
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels.
///
/// This behaves the same as [`ChainMonitor::new`] except that it relies on
/// [`MonitorUpdatingPersisterAsync`] and thus allows persistence to be completed async.
///
/// Note that async monitor updating is considered beta, and bugs may be triggered by its use.
pub fn new_async_beta(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are now three ways to do persistence: sync, the previous async way via implementing a different Persist and this new_async_beta?

Is there any form of consolidation possible between the two async setups?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I mention it in the last commit, but I think the eventual consolidation should be that we merge MonitorUpdatingPersister into ChainMonitor and then the Persist interface is just the interface between ChannelManager and ChainMonitor, a user will always just instantiate a ChainMonitor with either a KVStore or a KVStoreSync and we'll deal with the rest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I just wondered if we should already now steer towards MonitorUpdatingPersister with an async kv store as the only way to do async. I don't think it is more "beta" than the current callback-based async?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd missed this. I don't really see a strong reason to change the current API and remove the manual-async approach immediately. Its not additional code to maintain (given the new logic uses it under the hood anyway) and we do have folks using it. That said, it does probably make sense to deprecate it, which I'll go ahead and do here.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually nevermind, we should have a discussion about if we want to support async outside of rust, which would need the old API (or a way to make async KVStore work outside of rust, which I think we can do eventually as well).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't consider the bindings. Not great to remain stuck with multiple ways to do it, but not sure what we can do either.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can map the async stuff to bindings eventually, so its not like we're stuck, just a question of priorities.

chain_source: Option<C>, broadcaster: T, logger: L, feeest: F,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not simply fee_estimator?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause I copied the one from new 🤷‍♂️

persister: MonitorUpdatingPersisterAsync<K, S, L, ES, SP, T, F>, _entropy_source: ES,
_our_peerstorage_encryption_key: PeerStorageKey,
) -> Self {
Self {
monitors: RwLock::new(new_hash_map()),
chain_source,
broadcaster,
logger,
fee_estimator: feeest,
persister: AsyncPersister { persister },
_entropy_source,
pending_monitor_events: Mutex::new(Vec::new()),
highest_chain_height: AtomicUsize::new(0),
event_notifier: Notifier::new(),
pending_send_only_events: Mutex::new(Vec::new()),
#[cfg(peer_storage)]
our_peerstorage_encryption_key: _our_peerstorage_encryption_key,
}
}
}

impl<
ChannelSigner: EcdsaChannelSigner,
C: Deref,
Expand DownExpand Up@@ -1357,6 +1514,9 @@ where
fn release_pending_monitor_events(
&self,
) -> Vec<(OutPoint, ChannelId, Vec<MonitorEvent>, PublicKey)> {
for (channel_id, update_id) in self.persister.get_and_clear_completed_updates() {
Comment thread
joostjager marked this conversation as resolved.
let _ = self.channel_monitor_updated(channel_id, update_id);
}
let mut pending_monitor_events = self.pending_monitor_events.lock().unwrap().split_off(0);
for monitor_state in self.monitors.read().unwrap().values() {
let monitor_events = monitor_state.monitor.get_and_clear_pending_monitor_events();
Expand Down
Loading
Loading