Skip to content

Commit 4e82003

Browse files
authored
Merge pull request #611 from valentinewallace/fix-missing-htlc-claim
Tell ChannelMonitors about HTLCs fulfilled after channel close
2 parents 23a1d7a + 6f1a0bf commit 4e82003

File tree

6 files changed

+405
-68
lines changed

6 files changed

+405
-68
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ impl chain::Watch for TestChainMonitor {
128128
};
129129
let mut deserialized_monitor = <(BlockHash, channelmonitor::ChannelMonitor<EnforcingChannelKeys>)>::
130130
read(&mut Cursor::new(&map_entry.get().1)).unwrap().1;
131-
deserialized_monitor.update_monitor(&update, &&TestBroadcaster {}, &self.logger).unwrap();
131+
deserialized_monitor.update_monitor(&update, &&TestBroadcaster{}, &&FuzzEstimator{}, &self.logger).unwrap();
132132
let mut ser = VecWriter(Vec::new());
133133
deserialized_monitor.serialize_for_disk(&mut ser).unwrap();
134134
map_entry.insert((update.update_id, ser.0));

lightning/src/chain/chainmonitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ where C::Target: chain::Filter,
198198
},
199199
Some(orig_monitor) => {
200200
log_trace!(self.logger, "Updating Channel Monitor for channel {}", log_funding_info!(orig_monitor));
201-
let update_res = orig_monitor.update_monitor(&update, &self.broadcaster, &self.logger);
201+
let update_res = orig_monitor.update_monitor(&update, &self.broadcaster, &self.fee_estimator, &self.logger);
202202
if let Err(e) = &update_res {
203203
log_error!(self.logger, "Failed to update channel monitor: {:?}", e);
204204
}

lightning/src/chain/channelmonitor.rs

Lines changed: 149 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,29 @@ pub struct ChannelMonitorUpdate {
6464
pub(crate) updates: Vec<ChannelMonitorUpdateStep>,
6565
/// The sequence number of this update. Updates *must* be replayed in-order according to this
6666
/// sequence number (and updates may panic if they are not). The update_id values are strictly
67-
/// increasing and increase by one for each new update.
67+
/// increasing and increase by one for each new update, with one exception specified below.
6868
///
6969
/// This sequence number is also used to track up to which points updates which returned
7070
/// ChannelMonitorUpdateErr::TemporaryFailure have been applied to all copies of a given
7171
/// ChannelMonitor when ChannelManager::channel_monitor_updated is called.
72+
///
73+
/// The only instance where update_id values are not strictly increasing is the case where we
74+
/// allow post-force-close updates with a special update ID of [`CLOSED_CHANNEL_UPDATE_ID`]. See
75+
/// its docs for more details.
76+
///
77+
/// [`CLOSED_CHANNEL_UPDATE_ID`]: constant.CLOSED_CHANNEL_UPDATE_ID.html
7278
pub update_id: u64,
7379
}
7480

81+
/// If:
82+
/// (1) a channel has been force closed and
83+
/// (2) we receive a preimage from a forward link that allows us to spend an HTLC output on
84+
/// this channel's (the backward link's) broadcasted commitment transaction
85+
/// then we allow the `ChannelManager` to send a `ChannelMonitorUpdate` with this update ID,
86+
/// with the update providing said payment preimage. No other update types are allowed after
87+
/// force-close.
88+
pub const CLOSED_CHANNEL_UPDATE_ID: u64 = std::u64::MAX;
89+
7590
impl Writeable for ChannelMonitorUpdate {
7691
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
7792
self.update_id.write(w)?;
@@ -1144,8 +1159,47 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
11441159

11451160
/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
11461161
/// commitment_tx_infos which contain the payment hash have been revoked.
1147-
pub(crate) fn provide_payment_preimage(&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage) {
1162+
pub(crate) fn provide_payment_preimage<B: Deref, F: Deref, L: Deref>(&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage, broadcaster: &B, fee_estimator: &F, logger: &L)
1163+
where B::Target: BroadcasterInterface,
1164+
F::Target: FeeEstimator,
1165+
L::Target: Logger,
1166+
{
11481167
self.payment_preimages.insert(payment_hash.clone(), payment_preimage.clone());
1168+
1169+
// If the channel is force closed, try to claim the output from this preimage.
1170+
// First check if a counterparty commitment transaction has been broadcasted:
1171+
macro_rules! claim_htlcs {
1172+
($commitment_number: expr, $txid: expr) => {
1173+
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
1174+
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, None, broadcaster, fee_estimator, logger);
1175+
}
1176+
}
1177+
if let Some(txid) = self.current_counterparty_commitment_txid {
1178+
if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
1179+
claim_htlcs!(*commitment_number, txid);
1180+
return;
1181+
}
1182+
}
1183+
if let Some(txid) = self.prev_counterparty_commitment_txid {
1184+
if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
1185+
claim_htlcs!(*commitment_number, txid);
1186+
return;
1187+
}
1188+
}
1189+
1190+
// Then if a holder commitment transaction has been seen on-chain, broadcast transactions
1191+
// claiming the HTLC output from each of the holder commitment transactions.
1192+
// Note that we can't just use `self.holder_tx_signed`, because that only covers the case where
1193+
// *we* sign a holder commitment transaction, not when e.g. a watchtower broadcasts one of our
1194+
// holder commitment transactions.
1195+
if self.broadcasted_holder_revokable_script.is_some() {
1196+
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx);
1197+
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger);
1198+
if let Some(ref tx) = self.prev_holder_signed_commitment_tx {
1199+
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&tx);
1200+
self.onchain_tx_handler.update_claims_view(&Vec::new(), claim_reqs, None, broadcaster, fee_estimator, logger);
1201+
}
1202+
}
11491203
}
11501204

11511205
pub(crate) fn broadcast_latest_holder_commitment_txn<B: Deref, L: Deref>(&mut self, broadcaster: &B, logger: &L)
@@ -1162,26 +1216,45 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
11621216
/// itself.
11631217
///
11641218
/// panics if the given update is not the next update by update_id.
1165-
pub fn update_monitor<B: Deref, L: Deref>(&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, logger: &L) -> Result<(), MonitorUpdateError>
1166-
where B::Target: BroadcasterInterface,
1167-
L::Target: Logger,
1219+
pub fn update_monitor<B: Deref, F: Deref, L: Deref>(&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &L) -> Result<(), MonitorUpdateError>
1220+
where B::Target: BroadcasterInterface,
1221+
F::Target: FeeEstimator,
1222+
L::Target: Logger,
11681223
{
1169-
if self.latest_update_id + 1 != updates.update_id {
1224+
// ChannelMonitor updates may be applied after force close if we receive a
1225+
// preimage for a broadcasted commitment transaction HTLC output that we'd
1226+
// like to claim on-chain. If this is the case, we no longer have guaranteed
1227+
// access to the monitor's update ID, so we use a sentinel value instead.
1228+
if updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
1229+
match updates.updates[0] {
1230+
ChannelMonitorUpdateStep::PaymentPreimage { .. } => {},
1231+
_ => panic!("Attempted to apply post-force-close ChannelMonitorUpdate that wasn't providing a payment preimage"),
1232+
}
1233+
assert_eq!(updates.updates.len(), 1);
1234+
} else if self.latest_update_id + 1 != updates.update_id {
11701235
panic!("Attempted to apply ChannelMonitorUpdates out of order, check the update_id before passing an update to update_monitor!");
11711236
}
11721237
for update in updates.updates.iter() {
11731238
match update {
11741239
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs } => {
1240+
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
11751241
if self.lockdown_from_offchain { panic!(); }
11761242
self.provide_latest_holder_commitment_tx_info(commitment_tx.clone(), htlc_outputs.clone())?
11771243
},
1178-
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { unsigned_commitment_tx, htlc_outputs, commitment_number, their_revocation_point } =>
1179-
self.provide_latest_counterparty_commitment_tx_info(&unsigned_commitment_tx, htlc_outputs.clone(), *commitment_number, *their_revocation_point, logger),
1180-
ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage } =>
1181-
self.provide_payment_preimage(&PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()), &payment_preimage),
1182-
ChannelMonitorUpdateStep::CommitmentSecret { idx, secret } =>
1183-
self.provide_secret(*idx, *secret)?,
1244+
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { unsigned_commitment_tx, htlc_outputs, commitment_number, their_revocation_point } => {
1245+
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
1246+
self.provide_latest_counterparty_commitment_tx_info(&unsigned_commitment_tx, htlc_outputs.clone(), *commitment_number, *their_revocation_point, logger)
1247+
},
1248+
ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage } => {
1249+
log_trace!(logger, "Updating ChannelMonitor with payment preimage");
1250+
self.provide_payment_preimage(&PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()), &payment_preimage, broadcaster, fee_estimator, logger)
1251+
},
1252+
ChannelMonitorUpdateStep::CommitmentSecret { idx, secret } => {
1253+
log_trace!(logger, "Updating ChannelMonitor with commitment secret");
1254+
self.provide_secret(*idx, *secret)?
1255+
},
11841256
ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } => {
1257+
log_trace!(logger, "Updating ChannelMonitor: channel force closed, should broadcast: {}", should_broadcast);
11851258
self.lockdown_from_offchain = true;
11861259
if *should_broadcast {
11871260
self.broadcast_latest_holder_commitment_txn(broadcaster, logger);
@@ -1425,39 +1498,55 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
14251498
check_htlc_fails!(txid, "previous", 'prev_loop);
14261499
}
14271500

1501+
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
1502+
for req in htlc_claim_reqs {
1503+
claimable_outpoints.push(req);
1504+
}
1505+
1506+
}
1507+
(claimable_outpoints, (commitment_txid, watch_outputs))
1508+
}
1509+
1510+
fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec<ClaimRequest> {
1511+
let mut claims = Vec::new();
1512+
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&commitment_txid) {
14281513
if let Some(revocation_points) = self.their_cur_revocation_points {
14291514
let revocation_point_option =
1515+
// If the counterparty commitment tx is the latest valid state, use their latest
1516+
// per-commitment point
14301517
if revocation_points.0 == commitment_number { Some(&revocation_points.1) }
14311518
else if let Some(point) = revocation_points.2.as_ref() {
1519+
// If counterparty commitment tx is the state previous to the latest valid state, use
1520+
// their previous per-commitment point (non-atomicity of revocation means it's valid for
1521+
// them to temporarily have two valid commitment txns from our viewpoint)
14321522
if revocation_points.0 == commitment_number + 1 { Some(point) } else { None }
14331523
} else { None };
14341524
if let Some(revocation_point) = revocation_point_option {
1435-
self.counterparty_payment_script = {
1436-
// Note that the Network here is ignored as we immediately drop the address for the
1437-
// script_pubkey version
1438-
let payment_hash160 = WPubkeyHash::hash(&self.keys.pubkeys().payment_point.serialize());
1439-
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script()
1440-
};
1441-
1442-
// Then, try to find htlc outputs
1443-
for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() {
1525+
for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
14441526
if let Some(transaction_output_index) = htlc.transaction_output_index {
1445-
if transaction_output_index as usize >= tx.output.len() ||
1446-
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
1447-
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
1527+
if let Some(transaction) = tx {
1528+
if transaction_output_index as usize >= transaction.output.len() ||
1529+
transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
1530+
return claims; // Corrupted per_commitment_data, fuck this user
1531+
}
14481532
}
1449-
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
1533+
let preimage =
1534+
if htlc.offered {
1535+
if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) {
1536+
Some(*p)
1537+
} else { None }
1538+
} else { None };
14501539
let aggregable = if !htlc.offered { false } else { true };
14511540
if preimage.is_some() || !htlc.offered {
14521541
let witness_data = InputMaterial::CounterpartyHTLC { per_commitment_point: *revocation_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, preimage, htlc: htlc.clone() };
1453-
claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
1542+
claims.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
14541543
}
14551544
}
14561545
}
14571546
}
14581547
}
14591548
}
1460-
(claimable_outpoints, (commitment_txid, watch_outputs))
1549+
claims
14611550
}
14621551

14631552
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -1487,9 +1576,11 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
14871576
(claimable_outpoints, Some((htlc_txid, outputs)))
14881577
}
14891578

1490-
fn broadcast_by_holder_state(&self, commitment_tx: &Transaction, holder_tx: &HolderSignedTx) -> (Vec<ClaimRequest>, Vec<(u32, TxOut)>, Option<(Script, PublicKey, PublicKey)>) {
1579+
// Returns (1) `ClaimRequest`s that can be given to the OnChainTxHandler, so that the handler can
1580+
// broadcast transactions claiming holder HTLC commitment outputs and (2) a holder revokable
1581+
// script so we can detect whether a holder transaction has been seen on-chain.
1582+
fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx) -> (Vec<ClaimRequest>, Option<(Script, PublicKey, PublicKey)>) {
14911583
let mut claim_requests = Vec::with_capacity(holder_tx.htlc_outputs.len());
1492-
let mut watch_outputs = Vec::with_capacity(holder_tx.htlc_outputs.len());
14931584

14941585
let redeemscript = chan_utils::get_revokeable_redeemscript(&holder_tx.revocation_key, self.on_holder_tx_csv, &holder_tx.delayed_payment_key);
14951586
let broadcasted_holder_revokable_script = Some((redeemscript.to_v0_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));
@@ -1508,11 +1599,21 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
15081599
} else { None },
15091600
amount: htlc.amount_msat,
15101601
}});
1511-
watch_outputs.push((transaction_output_index, commitment_tx.output[transaction_output_index as usize].clone()));
15121602
}
15131603
}
15141604

1515-
(claim_requests, watch_outputs, broadcasted_holder_revokable_script)
1605+
(claim_requests, broadcasted_holder_revokable_script)
1606+
}
1607+
1608+
// Returns holder HTLC outputs to watch and react to in case of spending.
1609+
fn get_broadcasted_holder_watch_outputs(&self, holder_tx: &HolderSignedTx, commitment_tx: &Transaction) -> Vec<(u32, TxOut)> {
1610+
let mut watch_outputs = Vec::with_capacity(holder_tx.htlc_outputs.len());
1611+
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
1612+
if let Some(transaction_output_index) = htlc.transaction_output_index {
1613+
watch_outputs.push((transaction_output_index, commitment_tx.output[transaction_output_index as usize].clone()));
1614+
}
1615+
}
1616+
watch_outputs
15161617
}
15171618

15181619
/// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet)
@@ -1547,10 +1648,10 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
15471648
}
15481649

15491650
macro_rules! append_onchain_update {
1550-
($updates: expr) => {
1651+
($updates: expr, $to_watch: expr) => {
15511652
claim_requests = $updates.0;
1552-
watch_outputs.append(&mut $updates.1);
1553-
self.broadcasted_holder_revokable_script = $updates.2;
1653+
self.broadcasted_holder_revokable_script = $updates.1;
1654+
watch_outputs.append(&mut $to_watch);
15541655
}
15551656
}
15561657

@@ -1560,14 +1661,16 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
15601661
if self.current_holder_commitment_tx.txid == commitment_txid {
15611662
is_holder_tx = true;
15621663
log_trace!(logger, "Got latest holder commitment tx broadcast, searching for available HTLCs to claim");
1563-
let mut res = self.broadcast_by_holder_state(tx, &self.current_holder_commitment_tx);
1564-
append_onchain_update!(res);
1664+
let res = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx);
1665+
let mut to_watch = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, tx);
1666+
append_onchain_update!(res, to_watch);
15651667
} else if let &Some(ref holder_tx) = &self.prev_holder_signed_commitment_tx {
15661668
if holder_tx.txid == commitment_txid {
15671669
is_holder_tx = true;
15681670
log_trace!(logger, "Got previous holder commitment tx broadcast, searching for available HTLCs to claim");
1569-
let mut res = self.broadcast_by_holder_state(tx, holder_tx);
1570-
append_onchain_update!(res);
1671+
let res = self.get_broadcasted_holder_claims(holder_tx);
1672+
let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_tx, tx);
1673+
append_onchain_update!(res, to_watch);
15711674
}
15721675
}
15731676

@@ -1735,7 +1838,8 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
17351838
self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0));
17361839
if let Some(commitment_tx) = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript) {
17371840
self.holder_tx_signed = true;
1738-
let (mut new_outpoints, new_outputs, _) = self.broadcast_by_holder_state(&commitment_tx, &self.current_holder_commitment_tx);
1841+
let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx);
1842+
let new_outputs = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, &commitment_tx);
17391843
if !new_outputs.is_empty() {
17401844
watch_outputs.push((self.current_holder_commitment_tx.txid.clone(), new_outputs));
17411845
}
@@ -1763,7 +1867,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
17631867
}
17641868
}
17651869

1766-
self.onchain_tx_handler.block_connected(&txn_matched, claimable_outpoints, height, &*broadcaster, &*fee_estimator, &*logger);
1870+
self.onchain_tx_handler.update_claims_view(&txn_matched, claimable_outpoints, Some(height), &&*broadcaster, &&*fee_estimator, &&*logger);
17671871
self.last_block_hash = block_hash;
17681872

17691873
// Determine new outputs to watch by comparing against previously known outputs to watch,
@@ -2486,16 +2590,18 @@ mod tests {
24862590
use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
24872591
use ln::chan_utils;
24882592
use ln::chan_utils::{HTLCOutputInCommitment, HolderCommitmentTransaction};
2489-
use util::test_utils::TestLogger;
2593+
use util::test_utils::{TestLogger, TestBroadcaster, TestFeeEstimator};
24902594
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
24912595
use bitcoin::secp256k1::Secp256k1;
2492-
use std::sync::Arc;
2596+
use std::sync::{Arc, Mutex};
24932597
use chain::keysinterface::InMemoryChannelKeys;
24942598

24952599
#[test]
24962600
fn test_prune_preimages() {
24972601
let secp_ctx = Secp256k1::new();
24982602
let logger = Arc::new(TestLogger::new());
2603+
let broadcaster = Arc::new(TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())});
2604+
let fee_estimator = Arc::new(TestFeeEstimator { sat_per_kw: 253 });
24992605

25002606
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
25012607
let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
@@ -2571,7 +2677,7 @@ mod tests {
25712677
monitor.provide_latest_counterparty_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[17..20]), 281474976710653, dummy_key, &logger);
25722678
monitor.provide_latest_counterparty_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[18..20]), 281474976710652, dummy_key, &logger);
25732679
for &(ref preimage, ref hash) in preimages.iter() {
2574-
monitor.provide_payment_preimage(hash, preimage);
2680+
monitor.provide_payment_preimage(hash, preimage, &broadcaster, &fee_estimator, &logger);
25752681
}
25762682

25772683
// Now provide a secret, pruning preimages 10-15

0 commit comments

Comments
 (0)