-
Notifications
You must be signed in to change notification settings - Fork 411
Electrum interface for ChannelMonitor #858
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a899965
561ddc0
24351f5
5610ca1
2db1f1f
13a3340
8e37448
65e588f
c57bf73
f57c885
34792d0
1c72489
87f74fd
d45b38f
5e8b683
524c532
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,12 +24,13 @@ | |
//! servicing [`ChannelMonitor`] updates from the client. | ||
|
||
use bitcoin::blockdata::block::{Block, BlockHeader}; | ||
use bitcoin::hash_types::Txid; | ||
|
||
use chain; | ||
use chain::{Filter, WatchedOutput}; | ||
use chain::chaininterface::{BroadcasterInterface, FeeEstimator}; | ||
use chain::channelmonitor; | ||
use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, MonitorEvent, Persist}; | ||
use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, MonitorEvent, Persist, TransactionOutputs}; | ||
use chain::transaction::{OutPoint, TransactionData}; | ||
use chain::keysinterface::Sign; | ||
use util::logger::Logger; | ||
|
@@ -82,24 +83,77 @@ where C::Target: chain::Filter, | |
/// descendants of such transactions. It is not necessary to re-fetch the block to obtain | ||
/// updated `txdata`. | ||
pub fn block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) { | ||
self.process_chain_data(header, txdata, |monitor, txdata| { | ||
monitor.block_connected( | ||
header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &*self.logger) | ||
}); | ||
} | ||
|
||
/// Dispatches to per-channel monitors, which are responsible for updating their on-chain view | ||
/// of a channel and reacting accordingly to newly confirmed transactions. For details, see | ||
/// [`ChannelMonitor::transactions_confirmed`]. | ||
/// | ||
/// Used instead of [`block_connected`] by clients that are notified of transactions rather than | ||
/// blocks. May be called before or after [`update_best_block`] for transactions in the | ||
/// corresponding block. See [`update_best_block`] for further calling expectations. | ||
/// | ||
/// [`block_connected`]: Self::block_connected | ||
/// [`update_best_block`]: Self::update_best_block | ||
pub fn transactions_confirmed(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) { | ||
self.process_chain_data(header, txdata, |monitor, txdata| { | ||
monitor.transactions_confirmed( | ||
header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &*self.logger) | ||
}); | ||
} | ||
|
||
/// Dispatches to per-channel monitors, which are responsible for updating their on-chain view | ||
/// of a channel and reacting accordingly based on the new chain tip. For details, see | ||
/// [`ChannelMonitor::update_best_block`]. | ||
/// | ||
/// Used instead of [`block_connected`] by clients that are notified of transactions rather than | ||
/// blocks. May be called before or after [`transactions_confirmed`] for the corresponding | ||
/// block. | ||
/// | ||
/// Must be called after new blocks become available for the most recent block. Intermediary | ||
/// blocks, however, may be safely skipped. In the event of a chain re-organization, this only | ||
/// needs to be called for the most recent block assuming `transaction_unconfirmed` is called | ||
/// for any affected transactions. | ||
/// | ||
/// [`block_connected`]: Self::block_connected | ||
/// [`transactions_confirmed`]: Self::transactions_confirmed | ||
/// [`transaction_unconfirmed`]: Self::transaction_unconfirmed | ||
pub fn update_best_block(&self, header: &BlockHeader, height: u32) { | ||
self.process_chain_data(header, &[], |monitor, txdata| { | ||
// While in practice there shouldn't be any recursive calls when given empty txdata, | ||
// it's still possible if a chain::Filter implementation returns a transaction. | ||
debug_assert!(txdata.is_empty()); | ||
monitor.update_best_block( | ||
header, height, &*self.broadcaster, &*self.fee_estimator, &*self.logger) | ||
}); | ||
} | ||
|
||
fn process_chain_data<FN>(&self, header: &BlockHeader, txdata: &TransactionData, process: FN) | ||
where | ||
FN: Fn(&ChannelMonitor<ChannelSigner>, &TransactionData) -> Vec<TransactionOutputs> | ||
{ | ||
let mut dependent_txdata = Vec::new(); | ||
let monitors = self.monitors.read().unwrap(); | ||
for monitor in monitors.values() { | ||
let mut txn_outputs = monitor.block_connected(header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &*self.logger); | ||
let mut txn_outputs = process(monitor, txdata); | ||
|
||
// Register any new outputs with the chain source for filtering, storing any dependent | ||
// transactions from within the block that previously had not been included in txdata. | ||
if let Some(ref chain_source) = self.chain_source { | ||
let block_hash = header.block_hash(); | ||
for (txid, outputs) in txn_outputs.drain(..) { | ||
for (idx, output) in outputs.iter() { | ||
for (txid, mut outputs) in txn_outputs.drain(..) { | ||
for (idx, output) in outputs.drain(..) { | ||
// Register any new outputs with the chain source for filtering and recurse | ||
// if it indicates that there are dependent transactions within the block | ||
// that had not been previously included in txdata. | ||
let output = WatchedOutput { | ||
block_hash: Some(block_hash), | ||
outpoint: OutPoint { txid, index: *idx as u16 }, | ||
script_pubkey: output.script_pubkey.clone(), | ||
outpoint: OutPoint { txid, index: idx as u16 }, | ||
script_pubkey: output.script_pubkey, | ||
}; | ||
if let Some(tx) = chain_source.register_output(output) { | ||
dependent_txdata.push(tx); | ||
|
@@ -114,7 +168,7 @@ where C::Target: chain::Filter, | |
dependent_txdata.sort_unstable_by_key(|(index, _tx)| *index); | ||
dependent_txdata.dedup_by_key(|(index, _tx)| *index); | ||
let txdata: Vec<_> = dependent_txdata.iter().map(|(index, tx)| (*index, tx)).collect(); | ||
self.block_connected(header, &txdata, height); | ||
self.process_chain_data(header, &txdata, process); | ||
} | ||
} | ||
|
||
|
@@ -128,6 +182,36 @@ where C::Target: chain::Filter, | |
} | ||
} | ||
|
||
/// Dispatches to per-channel monitors, which are responsible for updating their on-chain view | ||
/// of a channel based on transactions unconfirmed as a result of a chain reorganization. See | ||
/// [`ChannelMonitor::transaction_unconfirmed`] for details. | ||
/// | ||
/// Used instead of [`block_disconnected`] by clients that are notified of transactions rather | ||
/// than blocks. May be called before or after [`update_best_block`] for transactions in the | ||
/// corresponding block. See [`update_best_block`] for further calling expectations. | ||
/// | ||
/// [`block_disconnected`]: Self::block_disconnected | ||
/// [`update_best_block`]: Self::update_best_block | ||
pub fn transaction_unconfirmed(&self, txid: &Txid) { | ||
let monitors = self.monitors.read().unwrap(); | ||
for monitor in monitors.values() { | ||
monitor.transaction_unconfirmed(txid, &*self.broadcaster, &*self.fee_estimator, &*self.logger); | ||
} | ||
} | ||
|
||
/// Returns the set of txids that should be monitored for re-organization out of the chain. | ||
pub fn get_relevant_txids(&self) -> Vec<Txid> { | ||
let mut txids = Vec::new(); | ||
let monitors = self.monitors.read().unwrap(); | ||
for monitor in monitors.values() { | ||
txids.append(&mut monitor.get_relevant_txids()); | ||
} | ||
|
||
txids.sort_unstable(); | ||
txids.dedup(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think here we're fetching from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, I think that I recall a similar conversation. When you say, "we" do you mean in rust-lightning or more broadly as part of a spec requirement? I ask because we may need to handle an atypical transaction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The spec doesn't forbid aggregating transactions from different channels for example penalty ones or second-stage HTLCs. And we don't implement it though it could be a potential fee optimization in the future, though a bit tricky to get right. IIRC last time we concluded we don't do it for now, but we're likely going to require dedup in the future, e.g dual-funding transaction initiating many channels. So sorry overriding first comment, let's just keep it imo. What do you mean by an atypical transaction ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah, I may have had a bit of confusion around who is generating the transactions. If we are always the one, then we won't need to dedup if we don't produce such transactions yet. But if our counterparty can also produces the transactions, then we may have to dedup if their implementation optimizes in this way. I think that's what I was trying to get at before.
Seems like it is fine to keep the dedup if we'll eventually need it. Let me know if you prefer to drop it. It should mostly be linear in the number of channels and only involves scanning (not shifting) if we don't produce dups.
Just as what you described above. |
||
txids | ||
} | ||
|
||
/// Creates a new `ChainMonitor` used to watch on-chain activity pertaining to channels. | ||
/// | ||
/// When an optional chain source implementing [`chain::Filter`] is provided, the chain monitor | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be a bit ambiguous whether it means are_transactions_confirmed or transations_got_confirmed. Not sure I have a good idea for a better name, but food for thought?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think the intended meaning for this and
block_connected
is that they are named as events with an implicit "handle" prefix. Let's punt a naming discussion to the follow-up PR.