Fetch shutdown script based on commit_upfront_shutdown_pubkey#1019
Fetch shutdown script based on commit_upfront_shutdown_pubkey#1019TheBlueMatt merged 11 commits intolightningdevkit:mainfrom
commit_upfront_shutdown_pubkey#1019Conversation
Codecov Report
@@ Coverage Diff @@
## main #1019 +/- ##
==========================================
+ Coverage 90.79% 90.92% +0.12%
==========================================
Files 61 62 +1
Lines 31578 32253 +675
==========================================
+ Hits 28672 29326 +654
- Misses 2906 2927 +21
Continue to review full report at Codecov.
|
e75d748 to
c0bf4fd
Compare
05d73ed to
83f3245
Compare
83f3245 to
ade99f8
Compare
|
Oops, this needs a similar test fix in the background-processor as well - https://git.bitcoin.ninja/index.cgi?p=rust-lightning;a=commit;h=9218054becc3dcf690bdd8fcf819952ee783365a |
ade99f8 to
260f269
Compare
lightning/src/ln/script.rs
Outdated
| /// # Panics | ||
| /// | ||
| /// This function may panic if given a segwit program with an invalid length. | ||
| pub fn new_witness_program(version: NonZeroU8, program: &[u8]) -> Self { |
There was a problem hiding this comment.
I think we should return an Result here - version needs to be <= 16 so users can totally make it fail, we shouldn't panic in that case, no? Also, why do the conversion to u5 and then call new_witness_program? That seems like a very roundabout way to get there, just push the version followed by the program as done at https://docs.rs/bitcoin/0.27.0/src/bitcoin/blockdata/script.rs.html#280-290 (but do the push using push_int which does the right thing here, don't have to resort to manual opcode calculation https://docs.rs/bitcoin/0.27.0/src/bitcoin/blockdata/script.rs.html#706-720 )
There was a problem hiding this comment.
Also this will change soon upstream, not sure how that would impact this. rust-bitcoin/rust-bitcoin#617
There was a problem hiding this comment.
Yeah, must cleaner like that. Will need to swap NonZeroU8 for WitnessVersion when it is available upstream.
260f269 to
c8ffc05
Compare
lightning/src/ln/channelmanager.rs
Outdated
| }); | ||
| if let Some(monitor_update) = monitor_update { | ||
| if let Err(_) = self.chain_monitor.update_channel(chan_entry.get().get_funding_txo().unwrap(), monitor_update) { | ||
| // TODO: How should this be handled? |
There was a problem hiding this comment.
Should be handled like any other error - call handle_monitor_err to convert the error into a MsgHandleErrInternal, then store it in a variable outside the lock (like failed_htlcs and chan_option) and handle_error the error outside of the lock.
There was a problem hiding this comment.
Hmmm... in the case of an error should we still send the shutdown message above? Similarly, should an error short-circuit any remaining code (e.g., failing back HTLCs, broadcasting channel update)?
There was a problem hiding this comment.
in the case of an error should we still send the shutdown message above?
There are two errors - permanent ones and temporary ones. In the case of permenent, yes, we should force-close instead and send an error message, for temporary, we should still send the shutdown.
failing back HTLCs,
We must never, ever forget to fail back htlcs we were told to fail back.
broadcasting channel update
This is only if we force-closed the channel due to an error, I think, so we shouldn't get to this point today if there's a monitor update, but a monitor update could cause us to get there now (if its a permanent one).
There was a problem hiding this comment.
Ok, this turned out to be a bit more tricky than I imagined. Confirming my understanding below.
Change is in 49614d8. Let me know if that is what you had in mind and if there's a simpler way of going about this. Need to do the same for internal_shutdown.
There are two errors - permanent ones and temporary ones. In the case of permenent, yes, we should force-close instead and send an
errormessage,
These are handled by handle_monitor_err and handle_error, respectively, IIUC.
for temporary, we should still send the shutdown.
What I don't quite understand is why we would send shutdown if we aren't certain that our ChannelMonitor has been updated with the shutdown script yet. Is it because if TemporaryError ever becomes a PermanentError, we wouldn't care about the shutdown script since we are force closing in that case?
There was a problem hiding this comment.
Rebased and added for internal_shutdown, too: a024930
There was a problem hiding this comment.
Change is in 49614d8. Let me know if that is what you had in mind and if there's a simpler way of going about this. Need to do the same for internal_shutdown.
Nope, that's about right. These things are tricky to handle. I left a few specific comments on that commit.
These are handled by handle_monitor_err and handle_error, respectively, IIUC.
That's the MsgHandleErrInternal vs monitor-error distinction, not the permanent-vs-temporary distinction, both of which are handled by handle_monitor_err (and friends)
What I don't quite understand is why we would send shutdown if we aren't certain that our ChannelMonitor has been updated with the shutdown script yet. Is it because if TemporaryError ever becomes a PermanentError, we wouldn't care about the shutdown script since we are force closing in that case?
The Temporary->Permanent thing isn't a big deal, as you note. The real question is what happens if someone gives a Temporary update, and then takes longer to update their ChannelMonitor than we do doing the full closing_signed negotiation (because only after closing_signed negotiation could a transaction ever appear on chain with the script contained in the monitor update). Unlike most other monitor updates (at least RevokeAndACK), sending a Shutdown message isn't an immediate lock-in to a new states and the monitor doesn't need to know about it immediately, only before the closing transaction appears on chain.
If we wait for the update to complete here, then we have to add Shutdown resending, which I really don't want to do. An alternative, which is much easier, is to just wait for the monitor update to complete before ever sending a closing_signed, something I can shove in #985
9924683 to
621e31e
Compare
621e31e to
d808581
Compare
TheBlueMatt
left a comment
There was a problem hiding this comment.
The ChannelMonitorUpdate changes look good to me, will re-review the rest a bit later but the rest was already basically good IMO.
46075b3 to
79eb7da
Compare
valentinewallace
left a comment
There was a problem hiding this comment.
Some of the new logic in ChannelManager is still sinking in for me, but overall this is looking good! Finished a first complete pass
jkczyz
left a comment
There was a problem hiding this comment.
Thanks for all the good feedback! Will see what I can do about the missing test coverage. It's a matter how easy it is to induce some error conditions (e.g., monitor update failing, keys provider giving an incompatible script).
3f87283 to
d6aec00
Compare
| msg: shutdown_msg | ||
| }); | ||
|
|
||
| if chan_entry.get().is_shutdown() { |
There was a problem hiding this comment.
Is it the intended behavior to only broadcast a channel update if the channel was < ChannelState::FundingSent? Because I think this line will only return true if that's the situation 🤔 .
I think coverage is missing here too. I get that we're in "crunch time" though, so I'm pretty fine on punting tests if it comes to it
There was a problem hiding this comment.
I believe it may also occur if we already handled a shutdown from the counterparty but didn't yet send our own shutdown.
There was a problem hiding this comment.
Ah, actually now that I think of it, you're right. Since we lock channel_state, the situation that I mentioned can't occur.
There was a problem hiding this comment.
Yeah, I guess I just wanna get clear that this code block (and similar) are necessary, and what situation they would run in? Double checked and couldn't find any test coverage by adding prints
There was a problem hiding this comment.
I believe this code block is actually now unreachable. We return an Err in case we're ready to shut down here instead of getting into this block.
There was a problem hiding this comment.
Removed code block from internal_shutdown rather than here as discussed on Slack.
valentinewallace
left a comment
There was a problem hiding this comment.
I can't really find anything to comment on, this is shaping up from my PoV!
| @@ -843,6 +854,16 @@ impl<Signer: Sign> Channel<Signer> { | |||
| } | |||
| } else { None }; | |||
There was a problem hiding this comment.
Prob not worth checking to see if they set a script here (which would make them a buggy peer), right?
There was a problem hiding this comment.
They are allowed to support the feature but not necessarily set the shutdown script upfront, if I understand BOLT 2 correctly.
There was a problem hiding this comment.
They are allowed to support the feature but not necessarily set the shutdown script upfront,
IIUC, this is allowed unless both peers advertise support for the feature, in which case setting a script is required: from BOLT 2
I was concerned about the case where a peer didn't advertise the feature but still set a shutdown script up front, but checked the spec and that seems to be allowed 🤷
|
It looks like this was rebased without any conflict, was there a reason for the rebase I missed? |
d6aec00 to
fbf624e
Compare
jkczyz
left a comment
There was a problem hiding this comment.
Added the requested tests. For testing IncompatibleShutdownScript errors, I needed to add InitFeatures to NodeCfg and pass it when connecting peers (see separate commit 2e03385) since ChannelManager::close_channel pulls the features from per_peer_state. We could probably use this in some of the test utilities instead of passing &InitFeatures::known() everywhere, but that's a more involved follow-up.
It looks like this was rebased without any conflict, was there a reason for the rebase I missed?
I was probably proactively rebasing just in case. 🙂
| @@ -843,6 +854,16 @@ impl<Signer: Sign> Channel<Signer> { | |||
| } | |||
| } else { None }; | |||
There was a problem hiding this comment.
They are allowed to support the feature but not necessarily set the shutdown script upfront, if I understand BOLT 2 correctly.
fbf624e to
76307ce
Compare
Oops, in general is it possible to avoid this? It makes it hard to diff-tree between revisions to see what changed. |
| @@ -843,6 +854,16 @@ impl<Signer: Sign> Channel<Signer> { | |||
| } | |||
| } else { None }; | |||
There was a problem hiding this comment.
They are allowed to support the feature but not necessarily set the shutdown script upfront,
IIUC, this is allowed unless both peers advertise support for the feature, in which case setting a script is required: from BOLT 2
I was concerned about the case where a peer didn't advertise the feature but still set a shutdown script up front, but checked the spec and that seems to be allowed 🤷
| msg: shutdown_msg | ||
| }); | ||
|
|
||
| if chan_entry.get().is_shutdown() { |
There was a problem hiding this comment.
Yeah, I guess I just wanna get clear that this code block (and similar) are necessary, and what situation they would run in? Double checked and couldn't find any test coverage by adding prints
76307ce to
b24b4e9
Compare
BOLT 2 enumerates the script formats that may be used for a shutdown script. KeysInterface::get_shutdown_pubkey returns a PublicKey used to form one of the acceptable formats (P2WPKH). Add a ShutdownScript abstraction to encapsulate all accept formats and be backwards compatible with P2WPKH scripts serialized as the corresponding PublicKey.
KeysInterface::get_shutdown_pubkey is used to form P2WPKH shutdown scripts. However, BOLT 2 allows for a wider variety of scripts. Refactor KeysInterface to allow any supported script while still maintaining serialization backwards compatibility with P2WPKH script pubkeys stored simply as the PublicKey. Add an optional TLV field to Channel and ChannelMonitor to support the new format, but continue to serialize the legacy PublicKey format.
Similar to 2745bd5, this ensures that ChannelManager knows about the features its peers.
When a shutdown script is omitted from open_channel or accept_channel, it must be provided when sending shutdown. Generate the shutdown script at channel closing time in this case rather at channel opening. This requires producing a ChannelMonitorUpdate with the shutdown script since it is no longer known at ChannelMonitor creation.
When handling shutdown messages, Channel cannot move to ChannelState::ShutdownComplete. Remove the code in ChannelManager that adds a MessageSendEvent::BroadcastChannelUpdate in this case since it is unreachable.
b24b4e9 to
1d3861e
Compare
Rather than fetching a channel's shutdown script from
KeysInterfaceat creation time, do so based onChannelConfig::commit_upfront_shutdown_pubkey(i.e., either creation or shutdown time).Additionally, support any acceptable shutdown script pubkey as defined by BOLT 2 while maintaining backwards compatibility with the legacy
KeysInterface::get_shutdown_pubkey, which is replaced byKeysInterface::get_shutdown_scriptpubkey.This change requires storing an optional script as a TLV field for both
ChannelandChannelMonitor. Additionally, since the field is now optional, a newChannelMonitorUpdateStepis needed for when the script is generated at shutdown time.Fixes #994.