Replies: 11 comments 15 replies
-
|
Dartffi is not directly compatible with rust, but will work with rust as long als the interface provided by rust is C compatible. With ffigen you are able to generate the bindings based on a .h header file. |
Beta Was this translation helpful? Give feedback.
-
|
Using the Kotlin/Swift UniFFI output, it would be bindings to Flutter, which make them platform specific and thus limited to Android, iOS/MacOS initially. But I guess most users/demand is from mobile apps anyways and not Desktop (Windows/Linux), so I don't think it's a big concern and can be managed later by adding more platform-specific code still. The advantage it has over the other ways is that the Kotlin and Swift bindings probably need to be generated for native Android/Swift apps anyways and so are reusable for both Flutter as native mobile development. Since the payjoin protocol and Rust code itself don't require any platform specific things, it could indeed be done in pure Dart with dart:ffi and that would have the full cross-platform (Desktop and even server side or CLI's) advantage. But I think writing safe C compatible Rust wrappers for it is a bit more complex/tricky to implement than using native platform channels and ready to use autogenerated Swift/Kotlin packages. Both the Kotlin/Swift bindings to Flutter and dart:ffi approach have the advantage over FRB to be possible with only standard dependencies and not cause dependency incompatibilities as occur between different packages using different versions of FRB. A clearer comparison:
Considering the above, I think pure Dart with dart:ffi would be the ideal, since it doesn't require writing platform specific code while covering all platforms, even Dart servers and CLI's could be build with it too then. But... I think getting the Rust wrappers right and safe is a pretty difficult task, so for simplicity and least risks I have a slight preference for number 2. So, that's my 2 sats, hope it helps somewhat in deciding and happy to hear your thoughts/additions on this comparison. |
Beta Was this translation helpful? Give feedback.
-
Kotlin MultiplatformIn order to support @konstantinullrich's request for desktop while leveraging others' interest to use Kotlin Multiplatform (KMP) and our core UniFFI library that produces it, we may be able to bind flutter from the output of Kotlin Multiplatform. @kirillzh has already begun work binding pdk to kotlin multiplatform, so we know there's a reason to make KMP work anyway. Doing so seems like an appropriate path forward to support flutter desktop for @cake-tech while still doing as much code re-use as possible for the sake of correctness.
|
Beta Was this translation helpful? Give feedback.
-
|
I myself lean on dart:ffi suggestion from @kumulynja and @konstantinullrich The main issue raised was that this "requires manual implementation of C compatible Rust code" and "writing safe C compatible Rust wrappers might be tricky." But I think here's where serialization (like Protobuf) could simplify things: // C-style header definition
If I can sketch a theoretical flow with protobuf, I would say:
I think this is a viable middle-ground, also someone mentioned using this approach, and also limitations in production here: https://stackoverflow.com/questions/69474386/how-to-call-rust-functions-in-flutter-dart-via-ffi-but-with-convenience-and-s |
Beta Was this translation helpful? Give feedback.
-
|
Maintaining a manual dart wrapper is exactly what UniFFI lets us avoid. If we're willing to write custom dart:ffi, a better long term solution would be to bite the bullet and write and maintain a dart plugin for UniFFI sufficient for our use case so that we can share code for it and other languages we wish to target. This would have knock-on effects to help bdk-ffi, bitcoin-ffi, etc, since they're all based on UniFFI anyway. A proof of concept to bind to dart via uniffi plugin would start with the existing implmentations here and here and produce a proof of concept. To bind |
Beta Was this translation helpful? Give feedback.
-
|
@spacebear21 and I have begun implementing the dart bindings through the use of uniffi-dart. I think based on the interaction with the project's maintainer it seems like there is some reasonable expectation of maintenance for it into the future. However, we have created a divergent branch from main fixing many of the missing features as well as upgrading the uniffi dependency there to 0.29 to be inline with the existing uniffi version in our payjoin-ffi crate that will hopefully be moved upstream into the future to reduce our maintenance burden. While I have created a minimal build validating that the auto-generated code has some level of correctness this is a very minimal build and many of the complex types exist outside of the uri crate I used. A real test will really need to wait for more code to be generated without errors to create more thorough integrations. While there was a lot of cleanly generated code a few problem areas remained on initial generation.
Remaining hurdles
|
Beta Was this translation helpful? Give feedback.
-
|
Another point that's worth mentioning: there will still be a role for payjoin-flutter after we complete the migration to uniffi-dart for bindings. uniffi-dart only produces dart (backend) bindings. payjoin-flutter could become a strictly frontend reference that uses uniffi-dart bindings instead of flutter_rust_bridge to produce an example app. |
Beta Was this translation helpful? Give feedback.
-
|
Quick update: I recently worked with @spacebear21 on implementing 'custom types support' for uniffi-dart (one of the missing features mentioned). We successfully got uniffi 0.29 custom type working with a typedef-based approach that prioritizes performance over compile-time safety. The implementation should be ready for upstreaming... Trade-offs question: Our typedef approach means: typedef Handle = i64;
// Handle and i64 are interchangeable at runtime - no compile-time type safetyvs a wrapper approach that would provide stronger guarantees: class Handle {
final i64 value;
// Handle(123) ≠ 123 - true type safety but with wrapper overhead
}
|
Beta Was this translation helpful? Give feedback.
-
|
@DanGould Using UniFFI to create Kotlin and Swift bindings, and using UniFFI to create a Flutter package via FRB, is still the most efficient approach. I haven't seen things change dramatically in the ecosystem to enable more efficient and lower-maintenance options. The third option in your Excalidraw diagram, This is what is used for
It would be great if Dart FFI is ready to be used so that we can use it directly with UniFFI. There is a similar effort for React Native here: https://github.com/jhugman/uniffi-bindgen-react-native However, both are not ready to use at the moment. |
Beta Was this translation helpful? Give feedback.
-
|
dart FFI status update:
some outstanding items:
|
Beta Was this translation helpful? Give feedback.
-
|
The Dart FFI PR has been merged with passing unit and integration tests. uniffi-dart has been graciously transferred by the previous owner to a new org: https://github.com/Uniffi-Dart/uniffi-dart, and @chavic is continuing his work on uniffi-dart there. All changes required to get the payjoin integration working have been merged upstream. Next steps:
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
flutter_rust_bindgen, dart:ffi, uniffi to kotlin/swift + dart wrapper, or uniffi-dart plugin??
payjoin-flutteris being used to give Bull Bitcoin Mobile and Cake wallet access to flutter. Whether or not our current approach is viable long term comes into question as the underlying API gets more complicated and we wish to offer payjoin to other targets through different bindings and compilation targets as well.I've drawn up excalidraw payjoin-ffi architecture dependency diagrams to show some different options. Below, I go into details of options which may not be in the diagrams but have come up in various conversations on the subject. I'd like to discuss the viability of each approach, and consider Signal's Coding Guidelines's "High Level Priorities" to be a useful rubric for us to select a choice, since signal has similar security requirements and downstream targets to payjoin.
Flutter Rust Bridge
This is what we're using now, though imprecisely and with excess manual wrapping. According to the docs there is some way to let FRB directly generate most every function and struct binding automagically from
payjoin-ffi, but that's not the current architecture, which uses wrappers on BOTH rust and dart sides (ew), so a proof of concept needs to be done.FRB might be favored because it's automagic (though not the way we do it now), but the security story and dependency bloat leave me wondering if there is something better. I'm not sure what, if any, flutter specifics we want for bindings, or if just dart bindings are sufficient to target Bull Bitcoin mobile and Cake.
it'd also be trickier to test properly since we'd need to do a second bindings wrapper around payjoin-test-utils using FRB as well, and a lot of the work to make UniFFI work seems to need to be re-done to make it work with FRB.
Dart bindings to Kotlin / Swift UniFFI Output
@kumulynja claims to have made a proof of concept of this arch. Sharing one source of truth which defines uniffi bindings removes maintenance burden and makes me more comfortable that we've gotten things right. I haven't seen a PoC so I'm not sure how feasible it is.
dart:ffi@konstantinullrich claims dart:ffi can bind directly to the rust. Apparently Dart ffigen can be used to generate bindings, and @konstantinullrich volunteered to maintain them. I have a limited understanding of this approach.
UniFFI Dart plugin
It'd be great to be able to compile dart directly from our UniFFI implementation.
The readme mentions this ancient
uniffi-rs-dart, but that's 2 years old. Safe to say it's unmaintained.There seems to be an alternative,
uniffi-dartwhich is more regularly maintained, but we have to consider how long that maintainer will be around, and to what extent payjoin-dart consumers can maintain the plugin. Mozilla does not and will not maintain the dart plugin for the foreseeable future because they do not use it in their other UniFFI products.Beta Was this translation helpful? Give feedback.
All reactions