-
Notifications
You must be signed in to change notification settings - Fork 407
Fetch shutdown script based on commit_upfront_shutdown_pubkey
#1019
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
Fetch shutdown script based on commit_upfront_shutdown_pubkey
#1019
Conversation
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
msg: shutdown_msg | ||
}); | ||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
error
message,
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased and added for internal_shutdown
, too: a024930
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.
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
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.
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
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.
Basically LGTM
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.
Some of the new logic in ChannelManager is still sinking in for me, but overall this is looking good! Finished a first complete pass
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed code block from internal_shutdown
rather than here as discussed on Slack.
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
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.
ACK mod one last clarification. Thanks for the additional test coverage added!
@@ -843,6 +854,16 @@ impl<Signer: Sign> Channel<Signer> { | |||
} | |||
} else { None }; |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
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.
ACK post-squash.
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
KeysInterface
at 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
Channel
andChannelMonitor
. Additionally, since the field is now optional, a newChannelMonitorUpdateStep
is needed for when the script is generated at shutdown time.Fixes #994.