-
Notifications
You must be signed in to change notification settings - Fork 407
Add a new method read_chan_signer
to KeysInterface
#761
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
Add a new method read_chan_signer
to KeysInterface
#761
Conversation
bf09abd
to
a1aca64
Compare
Codecov Report
@@ Coverage Diff @@
## main #761 +/- ##
==========================================
- Coverage 91.29% 91.28% -0.02%
==========================================
Files 37 37
Lines 22791 22836 +45
==========================================
+ Hits 20808 20846 +38
- Misses 1983 1990 +7
Continue to review full report at Codecov.
|
|
What's the intention behind the throwaway |
a1aca64
to
c9be345
Compare
It exists purely to be dummy KeysInterface for when we don't have the real KeysInterface handy during deserialization. Honestly, its mostly me being a bit lazy in tests which don't have a KeysInterface readily available and not wanting to bother reconstructing them. In lightning-signer, using it would likely indicate not bothering to enforce anything on the deserialized version, which seems wrong? |
c9be345
to
03cb581
Compare
I threaded I'll look into the two remaining issues. |
Got the two tests working and opened a PR in this repo - #764. |
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.
Pretty straightforward or do you think there are points which need more discussion?
lightning/src/chain/keysinterface.rs
Outdated
/// Reads a `ChanKeySigner` for this `KeysInterface` from the given input stream. | ||
/// This is only called during deserialization of other objects which contain | ||
/// `ChannelKeys`-implementing objects (ie `ChannelMonitor`s and `ChannelManager`s) and must | ||
/// read exactly the bytes that `<Self::ChanKeySigner as Writeable>::write()` writes, or return |
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.
What if you upgrade/downgrade KeysInterface
between r/w ? An older implementation may discard some data chunks written by a newer implementation if version is done correctly but I think your requirement scope this ?
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.
Maybe say instead "should validate that the length of the data is as expected".
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 added a note about versioning and extra bytes.
@@ -626,7 +632,8 @@ pub struct ChannelMonitor<ChanSigner: ChannelKeys> { | |||
counterparty_payment_script: Script, | |||
shutdown_script: Script, | |||
|
|||
keys: ChanSigner, | |||
key_derivation_params: (u64, u64), |
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.
Wouldn't be better in a future move to replace those two values by a ChannelKeys identifier and store them behind the interface to never expose them in-memory ? Those values are consumed in ChannelKeys derivation and might it could be better to keep them private ?
Anyway it would be great to add a one-line comment about their purpose in ChannelMonitor to serve at onchain output description, without constraining the utxo signer to store ChannelKeys.
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'm not exactly sure what you're asking - are you suggesting we rename them "channel_keys_id" or similar? While our default channel keys stores actual key derivation info in them, that's certainly not a requirement, nor should key derivation information generally be particularly sensitive.
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.
With our current KeysManager::derive_channel_keys
, those two values are inputs to the commitment seed, which is used to derive per-channel private keys. Theoretically I think an attacker able to observe them in system memory will have an easier task to re-compute the commitment_seed
. In practice, we're already aware of this limitation as comment in keysinterface.rs
(L754) underscores it.
If we want to move from this situation, we can have the KeysManager
provide an arbitrary identifier number to the derive_channel_keys
caller. This identifier could be stored by ChannelMonitor
and pass back in any SpendableOutputDescriptor
. That way, you can have a key holder implementation on an external signing device, it consumes a SpendableOutputDescriptor
, derives the channel keys and gets back the onchain output, without having ever exposed the derivation params.
I think that's a side-point to the current proposed change, so not a blocker.
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.
These values are an arbitrary identifier. They happen to be 128 bits in size so that keys interface implementors can use them as cryptographic values if they want, but there's nothing stopping them from being just an ID. The documentation on it can be better, indeed, but I think the API is already fine.
03cb581
to
2a7d5d1
Compare
2a7d5d1
to
811f548
Compare
We only actually use two of the fields in ChannelKeys inside a ChannelMonitor - the holder revocation_basepoint and the derivation parameters. Both are relatively small, so there isn't a lot of reason to hold a full copy of the ChannelKeys (with most of the interaction with it being inside the OnchainTxHandler). Further, this will avoid calling read on a `ChannelKeys` twice, which is a somewhat strange API quirk.
It doesn't make sense to ever build a lightning node which doesn't ever write ChannelMonitors to disk, so having a ChannelKeys object which doesn't implement Writeable is nonsense. Here we require Writeable for all ChannelKeys objects, simplifying code generation for C bindings somewhat.
There's no reason to have ChannelMonitor::write_for_disk instead of just using the Writeable trait anymore. Previously, it was used to differentiate with `write_for_watchtower`, but support for watchtower-mode ChannelMonitors was never completed and the partial bits were removed long ago. This has the nice benefit of hitting the custom Writeable codepaths in C bindings instead of trying to hit trait-generics paths.
This adds a new method to the general cross-channel `KeysInterface` which requires it to handle the deserialization of per-channel signer objects. This allows the deserialization of per-channel signers to have more context available, which, in the case of the C bindings, includes the actual KeysInterface information itself.
This drops any direct calls to a generic `ChannelKeys::read()` and replaces it with the new `KeysInterface::read_chan_signer()`. Still, under the hood all of our own `KeysInterface::read_chan_signer()` implementations simply call out to a `Readable::read()` implemention.
811f548
to
990d1de
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.
Code Review ACK 990d1de.
@@ -4237,8 +4243,10 @@ impl<ChanSigner: ChannelKeys> Writeable for Channel<ChanSigner> { | |||
} | |||
} | |||
|
|||
impl<ChanSigner: ChannelKeys + Readable> Readable for Channel<ChanSigner> { | |||
fn read<R : ::std::io::Read>(reader: &mut R) -> Result<Self, DecodeError> { | |||
const MAX_ALLOC_SIZE: usize = 64*1024; |
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 know 64 kB is used across the codebase but what's the rational picking up this value ? Is it a upper bound promise made by rustc
default memory allocator or an assumption on the underlying system ?
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.
Its random, basically. Its a nice round number that is small enough that its hard to add up enough 64KB chunks to cause damage but big enough that we basically never hit it. Anything a few x higher or lower would probably work just as well.
Gonna go ahead and merge cause there was no objection and there's a lot of work to be done on top. |
This adds a new method to the general cross-channel
KeysInterface
which requires it to handle the deserialization of per-channel
signer objects. This allows the deserialization of per-channel
signers to have more context available, which, in the case of the
C bindings, includes the actual KeysInterface information itself.
I have bindings updates to go with this as well, but will run them as a separate PR. Those bindings updates (and this) are required to get
ChannelMonitor
deserialization working in bindings, so tagging 0.0.13.