-
Notifications
You must be signed in to change notification settings - Fork 411
De-generify Sign methods #861
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
Conversation
f0df509
to
13c733e
Compare
Codecov Report
@@ Coverage Diff @@
## main #861 +/- ##
==========================================
+ Coverage 90.60% 90.89% +0.29%
==========================================
Files 51 51
Lines 27295 28351 +1056
==========================================
+ Hits 24730 25769 +1039
- Misses 2565 2582 +17
Continue to review full report at Codecov.
|
Hmm... not sure why the router benchmarks are failing. They are also failing for me in master. Oh, they are not required. |
d36f3a8
to
198f27a
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.
Hmmmmm, is there a way to avoid the dropping of Sign: Clone
? I'm not sure there is a way to avoid requiring ChannelMonitor: Clone
in the Java bindings (as there is no other way to give java a reference to something in Rust that is only valid for the scope of a function call, which we require for ChannelMonitor), I just haven't spent the time to figure out if I want to upstream that or not :/.
If its really required, maybe there's some other clever way around it - obviously the "Rust-y" way of doing the dyn would be to enum a wrapper, though it requires a few layers of wrappers so may be more challenging here. Or maybe there's a way to have a dynamic clone-but-return-the-boxed-one thing that I'm not aware of (we could always define our own trait for that).
enum DynamicSigner {
A(SignerImplA), B(SignerImplB)
}
impl Sign for DynamicSigner {
fn f(&self) { match self { A(a) => a.f(), B(b) -> b.f() } }
}
Decided this is not needed, see |
Correct, if you are using a |
96f36e4
to
97e0704
Compare
Heh, sometimes rust is just really stupid, |
1f5b1ab
to
4290706
Compare
1b6e001
to
216f35c
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.
Looks good, just tiny stuff, really. In general, best to word-wrap your commit messages to ~80 chars.
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.
Sounds good to me, minor comments
lightning/src/ln/msgs.rs
Outdated
1 + | ||
1 + 1 + high_zero_bytes_dropped_length(self.amt_to_forward) + | ||
1 + 1 + high_zero_bytes_dropped_length(self.outgoing_cltv_value as u64) + | ||
1 + 1 + 8 // short_channel_id |
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's the variance there, smallest 17 bytes, biggest 27 bytes ?
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.
Theoretically, yes, but in practice outgoing_cltv_value
needs four bytes on mainnet.
lightning/src/ln/msgs.rs
Outdated
None => 0, | ||
Some(payment_data) => | ||
1 + 1 + high_zero_bytes_dropped_length(payment_data.total_msat) + | ||
32 // payment_secret |
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-style, what do you think about ?
diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs
index 5e65c9ec..477474d0 100644
--- a/lightning/src/ln/msgs.rs
+++ b/lightning/src/ln/msgs.rs
@@ -909,16 +909,11 @@ mod fuzzy_internal_msgs {
1 +
1 + 1 + high_zero_bytes_dropped_length(self.amt_to_forward) +
1 + 1 + high_zero_bytes_dropped_length(self.outgoing_cltv_value as u64) +
- match payment_data {
- None => 0,
- Some(payment_data) =>
- 1 + 1 + high_zero_bytes_dropped_length(payment_data.total_msat) +
- 32 // payment_secret
- }
+ if payment_data.is_none() { 0 }
+ else { 1 + 1 + high_zero_bytes_dropped_length(payment_data.total_msat) + 32 } // payment_secret
}
}
}
-
}
pub struct DecodedOnionErrorPacket {
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.
Well, payment_data
would need an unwrap
in the else branch, which I usually try to avoid if I need to do something in both some/none cases. Just aesthetics, so happy to change it if LDK is standardizing on the if/else form.
secp_ctx: Secp256k1<secp256k1::SignOnly>, | ||
secp_ctx: Secp256k1<secp256k1::All>, |
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.
Was this change deliberate? It doesn't seem to go with the commit message.
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.
Yes. I'll add to the commit message.
It just doesn't seem worth it to deal with both kinds of contexts separately, given that the caller might as well have an All
handy to be able to supply both for different calls.
I think it's also not possible to pass in an All
when Signing
is expected without generics, which we are trying to get away from here.
lightning/src/util/ser.rs
Outdated
pub fn size_hint(&mut self, size: usize) { | ||
self.0.reserve(size) | ||
} |
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 was looking into how reserve
is implemented. It seems it increases the capacity to the max of either the desired capacity or double the current capacity. This makes me wonder how useful size_hint
really is.
I suppose we may save some allocations if the writer has 0 capacity, but I'm wondering if the same thing can be accomplished while defining our public interfaces in terms of std::io::Write
instead of Writer
. For instance, if a message knows it size, we could do something like:
Writer::with_capacity(message.size_hint());
Rather than having the message call size_hint
on Writer
. Thoughts?
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.
We could do reserve_exact
.
But I am guessing that the caller almost always calls these with a zero sized writer, otherwise it's kind of ineffective. If this is called with a non-zero sized writer (e.g. if this object is a field in a larger struct), then, yes, the size_hint mechanism won't be very effective and we should think about moving to something like with_capacity
.
Looking at this further, it looks like size_hint
is called when serializing sub-structs quite a bit. I wonder how effective it really is for that purpose, given what you say, and also given that the enclosing object would have to estimate the size without the sub-object that also has a hint (otherwise it would be double counting).
Maybe @TheBlueMatt has more insight about how size_hint
is meant to be used.
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.
Hmm, in general it probably doesn't make sense to use it for sub-structs. It's mostly intended for where we know the exact size, which we generally do in all our P2P message sending, avoiding copying the data a few times. Luckily, the max Jeff referenced means we can't really do worse than the "normal" behavior with it, though.
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.
As for putting it in messages vs calling it on a Writer, I certainly don't care which we end to with, but it should only be one or the other, and this PR is already nontrivial in size.
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.
Hmm, in general it probably doesn't make sense to use it for sub-structs.
It seems to be getting called for sub-structs, given that it's called on non-empty Writer
instances during the tests. Should I open an issue to look into that?
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.
@TheBlueMatt Not sure I understand how that would look with regards to a ChannelMonitor serializing its parameterized Sign.
Right, I guess there's a few options - take a reference to the KeysManager
and call KeysManager::serialize_signer(sign: &Sign)
. Or we could go with a straightforward Sign::serialize() -> Vec<u8>
only.
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.
@devrandom Could
DynSigner
also have a field storing something likeFnOnce
that downcasts the boxed signer and calls write? ThenDynSigner
could implementWriteable
by calling this. The instantiator ofDynSigner
would need to give this function, but presumably that is possible since they are also giving boxed signer and thus know what type to downcast to.
Yes, that works. There are actually more options, since a generic Write
can be converted to a dyn Write
inside DynSigner
.
But in any case, moving Writeable
from BaseSign
to Sign
is definitely a valid approach. I am not worried about the sunk cost of having rewritten the Writeable
trait, happy to back that out. Should decide whether or not to do that based on whether we actually expect some advantage from being able to write to arbitrary destinations without going via a Vec intermediary representation.
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.
Here's a branch with the above implemented: https://github.com/rust-bitcoin/rust-lightning/compare/main...lightning-signer:degenerify1?expand=1
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.
Yes, that works.
Huh! I guess when the limits the language is applying are arbitrary, the workarounds are arbitrary too :).
That looks good to me, too, what do you think @jkczyz?
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.
Huh! I guess when the limits the language is applying are arbitrary, the workarounds are arbitrary too :).
That looks good to me, too, what do you think @jkczyz?
Works for me! Would be nice to move Writeable
off to where it is needed rather than making it a sub-trait, if that's possible. Wasn't quite clear if the workaround allowed for that. But either way seems clean enough.
I am not worried about the sunk cost of having rewritten the
Writeable
trait, happy to back that out.
@devrandom Regarding Writer
trait, I do like making that a struct and using std::io::Write
, but that is no longer applicable to this PR and can come at some later time, as you said. I think you meant Writer
here, at least.
This is ready for another look. |
The generic methods prevent Sign from being a dyn object. Use Secp256k1<All> as part of removing generics from Secp256k1 contexts passed into Sign methods.
Clone requires Sized, which prevents Sign from being a dyn object.
OK, the changes to This is ready for another review. |
Code Review ACK 0821fb3 |
#[test] | ||
pub fn dyn_sign() { | ||
let _signer: Box<dyn BaseSign>; | ||
} |
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.
nit: add newline
This allows all methods of
Sign
(herein renamed toBaseSign
) to be non-generic, which allows it to have a vtable and be adyn
object.In general, the following can prevent a trait for being used with
dyn
:Sized
, directly or indirectly (e.g. viaClone
)