-
Notifications
You must be signed in to change notification settings - Fork 379
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
C/C++ Bindings #618
C/C++ Bindings #618
Conversation
e80f27a
to
5daa4b0
Compare
92f61cf
to
f9c969d
Compare
Codecov Report
@@ Coverage Diff @@
## master #618 +/- ##
===========================================
- Coverage 91.38% 74.84% -16.54%
===========================================
Files 35 55 +20
Lines 21727 24437 +2710
===========================================
- Hits 19855 18290 -1565
- Misses 1872 6147 +4275
Continue to review full report at Codecov.
|
91b5eb4
to
0935d67
Compare
6373e67
to
b370eda
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.
No need to address these right away. I'm going to keep to send comments out in small bursts as I read through the code. Hopefully a few more rounds today.
// Really not sure where syn gets '=' from here - | ||
// it somehow represents '///' or '//!' |
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 it is because /// ...
and //! ..
are syntactic sugar for [doc = " ..."]
.
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. Funny it even parses them that way.
match token_iter.next().unwrap() { | ||
TokenTree::Literal(lit) => { | ||
let line = format!("{}", lit); | ||
if line.contains("(C-not exported)") { |
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.
Instead of muddying up our docs, could we define an attribute instead?
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. We could change the line, but I actually kinda like putting it in the docs - it lets us link people to the rustdoc output even if they're using the library via C.
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 sure I follow the part about linking docs.
What I meant was we should define attributes that we use to annotate rust-lightning. Then this code checks for that attribute rather than trying to parse Rust docs.
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.
Right, the use of docs serves a dual purpose - both to allow us to make decisions at codegen time, and to actually document the fact that something isn't exported. Currently I've been sending people to the docs.rs docs for C bindings as well, though we could try to change that.
for attr in attrs.iter() { | ||
let tokens_clone = attr.tokens.clone(); | ||
let mut token_iter = tokens_clone.into_iter(); | ||
if let Some(token) = token_iter.next() { |
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.
This may be clearer if the path was examined first before iterating over the tokens. Especially given we can represent NoExport
and Rename
using attributes rather than with special comments, thus examining the tokens is not always necessary.
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.
Would you mind providing me a brain dump of the following?
- High-level motivation for the transformation code
- Each transformation and rationale for it
This would help me get a better understanding of the design decisions and allow me to provide more useful feedback. I'm happy to take that and make a README markdown which we can evolve along the way. I think we'll want something like that for posterity anyhow.
Otherwise, I'm not sure I'm making good use of my time understanding and trying to discern the rationale behind the generated code. Staring at it is kinda frying my brain honestly. :P
|
||
use lightning::chain::chaininterface::FeeEstimator as lnFeeEstimator; | ||
impl lnFeeEstimator for FeeEstimator { | ||
fn get_est_sat_per_1000_weight(&self, confirmation_target: lightning::chain::chaininterface::ConfirmationTarget) -> 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.
For parameter types, you may be able to write this as lnConfirmationTarget
since the use
statement for it is given earlier.
Or as that only possible because ConfirmationTarget
is defined in this file?
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 in this case its because its defined in this file. There are a few cases where it isnt. While maybe we could add imports, its probably not worth the effort.
use bitcoin::secp256k1::key::PublicKey as SecpPublicKey; | ||
use bitcoin::secp256k1::key::SecretKey as SecpSecretKey; |
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 there any way we can take advantage of the fact that these are Rust bindings for C code?
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 in theory, yea, they wrap underlying ffi/C types explicitly and you can use those, though that then assumes the caller is in C and/or using libsecp under the hood. I presume that this would be annoying to deal with if you were not, and byte arrays are universal. That said, it does result in an extra copy or two, including for private keys, which kinda sucks.
lightning-c-bindings/src/ln/msgs.rs
Outdated
use lightning::ln::msgs::ChannelMessageHandler as lnChannelMessageHandler; | ||
impl lnChannelMessageHandler for ChannelMessageHandler { | ||
fn handle_open_channel(&self, their_node_id: &bitcoin::secp256k1::key::PublicKey, their_features: lightning::ln::features::InitFeatures, msg: &lightning::ln::msgs::OpenChannel) { | ||
(self.handle_open_channel)(self.this_arg, crate::c_types::PublicKey::from_rust(their_node_id), crate::ln::features::InitFeatures { inner: Box::into_raw(Box::new(their_features)) }, &OpenChannel { inner: msg }) |
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.
Why do types like PublicKey
need to be serialized while types like InitFeatures
do not?
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.
Fundamentally because there's a C equivalent (for a public key namely a 33 byte string). We could just serialize out InitFeatures into C (though its obviously more complicated since its not fixed-length), but then we'd lose things like "does_support_feature_X" (well, we dont have those now cause we cant run the macro to figure out the generated code, but at least for other types this holds).
bfa9148
to
3c85f1a
Compare
e654d7e
to
cdfc9d1
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.
Other than these last few nits, looks good!
|
||
LDKEventsProvider prov = ChannelManager_as_EventsProvider(&cm); | ||
LDKCVec_EventZ events = (prov.get_and_clear_pending_events)(prov.this_arg); | ||
assert((unsigned long)events.data < 4096); // There's an offset, but it should still be an offset against null in the 0 page |
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.
Wondering, what's the purpose of this check? (and it's casting events.data
as a pointer, 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.
It checks that there was no allocation/malloc done for the data.
cdfc9d1
to
bb3d6aa
Compare
bb3d6aa
to
9be38b9
Compare
I think this is ready to hit merge! |
You probably saw this, but the |
Oops, I'd accidentally dropped one commit in rebase, fixed now. |
This cleans upa few last cases of functions/objects which our C bindings generator doesn't know how to read.
In general, it maps: * Traits to a struct with a void* and a list of function pointers, emulating what the compiler will do for a dyn trait anyway, * Structs as a struct with a single opaque pointer to the underlying type and a flag to indicate ownership. While this is a bit less effecient than just a direct pointer, it neatly lets us expose in the public interface the concept of ownership by setting a flag in the generated struct. * Unit enums as enums with each type copied over and conversion functions, * Non-unit enums have each field converted back and forth with a type flag and a union across all the C-mapped fields.
Including: * A script to automatically generate all the rest, * Cargo.toml and cbindgen.toml, * manually-written wrapper types for a few types
af00220
to
0ab7f90
Compare
This is a super rough draft first pass at generating C bindings. The code to do so is horrendous, but the resulting C bindings are not completely insane. Thanks to @arik-so for initial passes at cbindgen and helping work through what could work and what downstream clients wont be able to use. Things left to do: