From 7a41cad20fe6494064460eaa54c238fff9e45779 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 5 Feb 2024 14:53:06 +0100 Subject: [PATCH] bindings: First version of the uniffi bindings --- Cargo.toml | 1 + libs/gl-client-bindings/Cargo.toml | 19 + libs/gl-client-bindings/Makefile | 34 ++ libs/gl-client-bindings/build.rs | 3 + libs/gl-client-bindings/src/error.rs | 12 + libs/gl-client-bindings/src/glclient.udl | 32 ++ .../gl-client-bindings/src/glclient.uniffi.rs | 377 ++++++++++++++++++ libs/gl-client-bindings/src/lib.rs | 11 + libs/gl-client-bindings/src/raw_client.rs | 42 ++ libs/gl-client-bindings/src/runtime.rs | 21 + libs/gl-client-bindings/src/scheduler.rs | 43 ++ 11 files changed, 595 insertions(+) create mode 100644 libs/gl-client-bindings/Cargo.toml create mode 100644 libs/gl-client-bindings/Makefile create mode 100644 libs/gl-client-bindings/build.rs create mode 100644 libs/gl-client-bindings/src/error.rs create mode 100644 libs/gl-client-bindings/src/glclient.udl create mode 100644 libs/gl-client-bindings/src/glclient.uniffi.rs create mode 100644 libs/gl-client-bindings/src/lib.rs create mode 100644 libs/gl-client-bindings/src/raw_client.rs create mode 100644 libs/gl-client-bindings/src/runtime.rs create mode 100644 libs/gl-client-bindings/src/scheduler.rs diff --git a/Cargo.toml b/Cargo.toml index 3a76299ec..be56b759f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "libs/gl-client-py", "libs/gl-plugin", "libs/gl-signerproxy", + "libs/gl-client-bindings", ] [workspace.dependencies] diff --git a/libs/gl-client-bindings/Cargo.toml b/libs/gl-client-bindings/Cargo.toml new file mode 100644 index 000000000..239333cfe --- /dev/null +++ b/libs/gl-client-bindings/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "gl-client-bindings" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] +name = "glclient" + +[dependencies] +anyhow.workspace = true +gl-client = { path = "../gl-client", features = ["permissive"] } +thiserror = "1.0.56" +tokio = { version = "1", features = [], default-features = false} +uniffi = { version = "0.24" } +once_cell = "*" + +[build-dependencies] +uniffi = { version = "0.24", features = [ "build" ] } diff --git a/libs/gl-client-bindings/Makefile b/libs/gl-client-bindings/Makefile new file mode 100644 index 000000000..3038f448f --- /dev/null +++ b/libs/gl-client-bindings/Makefile @@ -0,0 +1,34 @@ + +.PHONY: generate + +GENERATED := \ + src/glclient.py \ + src/glclient.rb \ + src/glclient.swift \ + src/glclient/glclient.go \ + src/glclient/glclient.c \ + src/glclient/glclient.h \ + src/uniffi/glclient/glclient.kt + +generate: src/glclient.py src/glclient.go src/glclient.rb src/glclient.swift src/glclient.kt + +src/glclient.swift: src/glclient.udl src/glclient.uniffi.rs + cargo run -p uniffi-bindgen --features=uniffi/cli generate src/glclient.udl --language swift --no-format + +src/glclient.rb: src/glclient.udl src/glclient.uniffi.rs + cargo run -p uniffi-bindgen --features=uniffi/cli generate src/glclient.udl --language ruby --no-format + +src/uniffi/glclient/glclient.kt: src/glclient.udl src/glclient.uniffi.rs + cargo run -p uniffi-bindgen --features=uniffi/cli generate src/glclient.udl --language kotlin --no-format + +src/glclient.py: src/glclient.udl src/glclient.uniffi.rs + cargo run -p uniffi-bindgen --features=uniffi/cli generate src/glclient.udl --language python --no-format + +src/glclient/glclient.h src/glclient/glclient.c src/glclientglclient.go: src/glclient.udl src/glclient.uniffi.rs + uniffi-bindgen-go src/glclient.udl --no-format + +src/glclient.uniffi.rs: src/glclient.udl + cargo run -p uniffi-bindgen --features=uniffi/cli scaffolding src/glclient.udl --no-format + +clean: + rm -fv ${GENERATED} diff --git a/libs/gl-client-bindings/build.rs b/libs/gl-client-bindings/build.rs new file mode 100644 index 000000000..b92bc0922 --- /dev/null +++ b/libs/gl-client-bindings/build.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi::generate_scaffolding("src/glclient.udl").unwrap(); +} diff --git a/libs/gl-client-bindings/src/error.rs b/libs/gl-client-bindings/src/error.rs new file mode 100644 index 000000000..4b6c92ece --- /dev/null +++ b/libs/gl-client-bindings/src/error.rs @@ -0,0 +1,12 @@ +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("unhandled error: {0}")] + Other(#[from] anyhow::Error), + #[error("Invalid argument error: {0}")] + InvalidArgument(Box), + #[error("Error while calling {method}: {error}")] + Call { + method: String, + error: String, + }, +} diff --git a/libs/gl-client-bindings/src/glclient.udl b/libs/gl-client-bindings/src/glclient.udl new file mode 100644 index 000000000..d9a2b3908 --- /dev/null +++ b/libs/gl-client-bindings/src/glclient.udl @@ -0,0 +1,32 @@ +namespace glclient { +}; + +[Error] +enum Error { + "Call", + "Other", + "InvalidArgument", +}; + +interface Scheduler { + + [Throws=Error] + constructor(bytes node_id, string network); + + [Throws=Error] + void authenticate(bytes cert_pem, bytes key_pem); +}; + +interface RawClient { + [Throws=Error] + constructor( + bytes node_id, + string network, + bytes cert_pem, + bytes key_pem, + string node_uri + ); + + [Throws=Error] + bytes call(string method, bytes payload); + }; diff --git a/libs/gl-client-bindings/src/glclient.uniffi.rs b/libs/gl-client-bindings/src/glclient.uniffi.rs new file mode 100644 index 000000000..5e265bf7a --- /dev/null +++ b/libs/gl-client-bindings/src/glclient.uniffi.rs @@ -0,0 +1,377 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + + +// Unit struct to parameterize the FfiConverter trait. +// +// We use FfiConverter to handle lowering/lifting/serializing types for this crate. See +// https://mozilla.github.io/uniffi-rs/internals/lifting_and_lowering.html#code-generation-and-the-fficonverter-trait +// for details. +// +// This is pub, since we need to access it to support external types +#[doc(hidden)] +pub struct UniFfiTag; + +#[allow(clippy::missing_safety_doc, missing_docs)] +#[doc(hidden)] +#[no_mangle] +pub extern "C" fn ffi_glclient_uniffi_contract_version() -> u32 { + 22 +} +/// Export namespace metadata. +/// +/// See `uniffi_bindgen::macro_metadata` for how this is used. + +const UNIFFI_META_CONST_NAMESPACE_GLCLIENT: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::NAMESPACE) + .concat_str(env!("CARGO_CRATE_NAME")) + .concat_str("glclient"); + +#[doc(hidden)] +#[no_mangle] +pub static UNIFFI_META_NAMESPACE_GLCLIENT: [u8; UNIFFI_META_CONST_NAMESPACE_GLCLIENT.size] = UNIFFI_META_CONST_NAMESPACE_GLCLIENT.into_array(); + +// Check for compatibility between `uniffi` and `uniffi_bindgen` versions. +// Note that we have an error message on the same line as the assertion. +// This is important, because if the assertion fails, the compiler only +// seems to show that single line as context for the user. +uniffi::assert_compatible_version!("0.24.3"); // Please check that you depend on version 0.24.3 of the `uniffi` crate. + + + + + + + + +// Everybody gets basic buffer support, since it's needed for passing complex types over the FFI. +// +// See `uniffi/src/ffi/rustbuffer.rs` for documentation on these functions + +#[allow(clippy::missing_safety_doc, missing_docs)] +#[doc(hidden)] +#[no_mangle] +pub extern "C" fn ffi_glclient_rustbuffer_alloc(size: i32, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer { + uniffi::ffi::uniffi_rustbuffer_alloc(size, call_status) +} + +#[allow(clippy::missing_safety_doc, missing_docs)] +#[doc(hidden)] +#[no_mangle] +pub unsafe extern "C" fn ffi_glclient_rustbuffer_from_bytes(bytes: uniffi::ForeignBytes, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer { + uniffi::ffi::uniffi_rustbuffer_from_bytes(bytes, call_status) +} + +#[allow(clippy::missing_safety_doc, missing_docs)] +#[doc(hidden)] +#[no_mangle] +pub unsafe extern "C" fn ffi_glclient_rustbuffer_free(buf: uniffi::RustBuffer, call_status: &mut uniffi::RustCallStatus) { + uniffi::ffi::uniffi_rustbuffer_free(buf, call_status); +} + +#[allow(clippy::missing_safety_doc, missing_docs)] +#[doc(hidden)] +#[no_mangle] +pub unsafe extern "C" fn ffi_glclient_rustbuffer_reserve(buf: uniffi::RustBuffer, additional: i32, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer { + uniffi::ffi::uniffi_rustbuffer_reserve(buf, additional, call_status) +} + + + +// Error definitions, corresponding to `error` in the UDL. + + +#[::uniffi::ffi_converter_error( + tag = crate::UniFfiTag, + flat_error, + +)] +enum r#Error { + r#Call { + }, + r#Other { + }, + r#InvalidArgument { + }, +} + + + +// Record definitions, implemented as method-less structs, corresponding to `dictionary` objects. + + +// Top level functions, corresponding to UDL `namespace` functions.// Object definitions, corresponding to UDL `interface` definitions. + +// For each Object definition, we assume the caller has provided an appropriately-shaped `struct T` +// with an `impl` for each method on the object. We create an `Arc` for "safely" handing out +// references to these structs to foreign language code, and we provide a `pub extern "C"` function +// corresponding to each method. +// +// (Note that "safely" is in "scare quotes" - that's because we use functions on an `Arc` that +// that are inherently unsafe, but the code we generate is safe in practice.) +// +// If the caller's implementation of the struct does not match with the methods or types specified +// in the UDL, then the rust compiler will complain with a (hopefully at least somewhat helpful!) +// error message when processing this generated code. +#[::uniffi::ffi_converter_interface(tag = crate::UniFfiTag)] +struct r#Scheduler { } + + +// All Object structs must be `Sync + Send`. The generated scaffolding will fail to compile +// if they are not, but unfortunately it fails with an unactionably obscure error message. +// By asserting the requirement explicitly, we help Rust produce a more scrutable error message +// and thus help the user debug why the requirement isn't being met. +uniffi::deps::static_assertions::assert_impl_all!(r#Scheduler: Sync, Send); + +#[doc(hidden)] +#[no_mangle] +pub extern "C" fn uniffi_glclient_fn_free_scheduler(ptr: *const std::os::raw::c_void, call_status: &mut uniffi::RustCallStatus) { + uniffi::rust_call(call_status, || { + assert!(!ptr.is_null()); + drop(unsafe { ::std::sync::Arc::from_raw(ptr as *const r#Scheduler) }); + + Ok(()) + }) +} + #[doc(hidden)] + #[no_mangle] + pub extern "C" fn r#uniffi_glclient_fn_constructor_scheduler_new( + r#node_id: ::uniffi::RustBuffer, + r#network: ::uniffi::RustBuffer, + call_status: &mut uniffi::RustCallStatus + ) -> *const std::os::raw::c_void /* *const Scheduler */ { + uniffi::deps::log::debug!("uniffi_glclient_fn_constructor_scheduler_new"); + + // If the constructor does not have the same signature as declared in the UDL, then + // this attempt to call it will fail with a (somewhat) helpful compiler error. + uniffi::rust_call(call_status, || { + <::std::result::Result, r#Error> as ::uniffi::FfiConverter>::lower_return( + r#Scheduler::r#new( + match as ::uniffi::FfiConverter>::try_lift(r#node_id) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "node_id")), + }, + match>::try_lift(r#network) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "network")), + }).map(::std::sync::Arc::new).map_err(Into::into) + ) + }) + } + +#[doc(hidden)] +#[no_mangle] +#[allow(clippy::let_unit_value,clippy::unit_arg)] // The generated code uses the unit type like other types to keep things uniform +pub extern "C" fn r#uniffi_glclient_fn_method_scheduler_authenticate( + r#ptr: *const std::os::raw::c_void, + r#cert_pem: ::uniffi::RustBuffer, + r#key_pem: ::uniffi::RustBuffer, + call_status: &mut uniffi::RustCallStatus +) { + uniffi::deps::log::debug!("uniffi_glclient_fn_method_scheduler_authenticate"); + uniffi::rust_call(call_status, || { + <::std::result::Result<(), r#Error> as ::uniffi::FfiConverter>::lower_return( + ::r#authenticate( + match as ::uniffi::FfiConverter>::try_lift(r#ptr) { + + Ok(ref val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "ptr")), + }, + match as ::uniffi::FfiConverter>::try_lift(r#cert_pem) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "cert_pem")), + }, + match as ::uniffi::FfiConverter>::try_lift(r#key_pem) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "key_pem")), + }) + + .map_err(Into::into) + ) + }) +} + + + +// For each Object definition, we assume the caller has provided an appropriately-shaped `struct T` +// with an `impl` for each method on the object. We create an `Arc` for "safely" handing out +// references to these structs to foreign language code, and we provide a `pub extern "C"` function +// corresponding to each method. +// +// (Note that "safely" is in "scare quotes" - that's because we use functions on an `Arc` that +// that are inherently unsafe, but the code we generate is safe in practice.) +// +// If the caller's implementation of the struct does not match with the methods or types specified +// in the UDL, then the rust compiler will complain with a (hopefully at least somewhat helpful!) +// error message when processing this generated code. +#[::uniffi::ffi_converter_interface(tag = crate::UniFfiTag)] +struct r#RawClient { } + + +// All Object structs must be `Sync + Send`. The generated scaffolding will fail to compile +// if they are not, but unfortunately it fails with an unactionably obscure error message. +// By asserting the requirement explicitly, we help Rust produce a more scrutable error message +// and thus help the user debug why the requirement isn't being met. +uniffi::deps::static_assertions::assert_impl_all!(r#RawClient: Sync, Send); + +#[doc(hidden)] +#[no_mangle] +pub extern "C" fn uniffi_glclient_fn_free_rawclient(ptr: *const std::os::raw::c_void, call_status: &mut uniffi::RustCallStatus) { + uniffi::rust_call(call_status, || { + assert!(!ptr.is_null()); + drop(unsafe { ::std::sync::Arc::from_raw(ptr as *const r#RawClient) }); + + Ok(()) + }) +} + #[doc(hidden)] + #[no_mangle] + pub extern "C" fn r#uniffi_glclient_fn_constructor_rawclient_new( + r#node_id: ::uniffi::RustBuffer, + r#network: ::uniffi::RustBuffer, + r#cert_pem: ::uniffi::RustBuffer, + r#key_pem: ::uniffi::RustBuffer, + r#node_uri: ::uniffi::RustBuffer, + call_status: &mut uniffi::RustCallStatus + ) -> *const std::os::raw::c_void /* *const RawClient */ { + uniffi::deps::log::debug!("uniffi_glclient_fn_constructor_rawclient_new"); + + // If the constructor does not have the same signature as declared in the UDL, then + // this attempt to call it will fail with a (somewhat) helpful compiler error. + uniffi::rust_call(call_status, || { + <::std::result::Result, r#Error> as ::uniffi::FfiConverter>::lower_return( + r#RawClient::r#new( + match as ::uniffi::FfiConverter>::try_lift(r#node_id) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "node_id")), + }, + match>::try_lift(r#network) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "network")), + }, + match as ::uniffi::FfiConverter>::try_lift(r#cert_pem) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "cert_pem")), + }, + match as ::uniffi::FfiConverter>::try_lift(r#key_pem) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "key_pem")), + }, + match>::try_lift(r#node_uri) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "node_uri")), + }).map(::std::sync::Arc::new).map_err(Into::into) + ) + }) + } + +#[doc(hidden)] +#[no_mangle] +#[allow(clippy::let_unit_value,clippy::unit_arg)] // The generated code uses the unit type like other types to keep things uniform +pub extern "C" fn r#uniffi_glclient_fn_method_rawclient_call( + r#ptr: *const std::os::raw::c_void, + r#method: ::uniffi::RustBuffer, + r#payload: ::uniffi::RustBuffer, + call_status: &mut uniffi::RustCallStatus +) -> as ::uniffi::FfiConverter>::ReturnType { + uniffi::deps::log::debug!("uniffi_glclient_fn_method_rawclient_call"); + uniffi::rust_call(call_status, || { + <::std::result::Result, r#Error> as ::uniffi::FfiConverter>::lower_return( + ::r#call( + match as ::uniffi::FfiConverter>::try_lift(r#ptr) { + + Ok(ref val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "ptr")), + }, + match>::try_lift(r#method) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "method")), + }, + match as ::uniffi::FfiConverter>::try_lift(r#payload) { + + Ok(val) => val, + Err(err) => return Err(uniffi::lower_anyhow_error_or_panic::(err, "payload")), + }) + + .map_err(Into::into) + ) + }) +} + + + + +// Callback Interface definitions, corresponding to UDL `callback interface` definitions. + + +// External and Wrapped types +// Support for external types. + +// Types with an external `FfiConverter`... + + +// For custom scaffolding types we need to generate an FfiConverter impl based on the +// UniffiCustomTypeConverter implementation that the library supplies + + +// Export scaffolding checksums + +#[no_mangle] +#[doc(hidden)] +pub extern "C" fn r#uniffi_glclient_checksum_method_scheduler_authenticate() -> u16 { + 33183 +} +#[no_mangle] +#[doc(hidden)] +pub extern "C" fn r#uniffi_glclient_checksum_method_rawclient_call() -> u16 { + 37495 +} +#[no_mangle] +#[doc(hidden)] +pub extern "C" fn r#uniffi_glclient_checksum_constructor_scheduler_new() -> u16 { + 2930 +} +#[no_mangle] +#[doc(hidden)] +pub extern "C" fn r#uniffi_glclient_checksum_constructor_rawclient_new() -> u16 { + 9265 +} + +// The `reexport_uniffi_scaffolding` macro +// Code to re-export the UniFFI scaffolding functions. +// +// Rust won't always re-export the functions from dependencies +// ([rust-lang#50007](https://github.com/rust-lang/rust/issues/50007)) +// +// A workaround for this is to have the dependent crate reference a function from its dependency in +// an extern "C" function. This is clearly hacky and brittle, but at least we have some unittests +// that check if this works (fixtures/reexport-scaffolding-macro). +// +// The main way we use this macro is for that contain multiple UniFFI components (libxul, +// megazord). The combined library has a cargo dependency for each component and calls +// uniffi_reexport_scaffolding!() for each one. + +#[allow(missing_docs)] +#[doc(hidden)] +pub const fn uniffi_reexport_hack() {} + +#[doc(hidden)] +#[macro_export] +macro_rules! uniffi_reexport_scaffolding { + () => { + #[doc(hidden)] + #[no_mangle] + pub extern "C" fn glclient_uniffi_reexport_hack() { + $crate::uniffi_reexport_hack() + } + }; +} \ No newline at end of file diff --git a/libs/gl-client-bindings/src/lib.rs b/libs/gl-client-bindings/src/lib.rs new file mode 100644 index 000000000..aa0d891a5 --- /dev/null +++ b/libs/gl-client-bindings/src/lib.rs @@ -0,0 +1,11 @@ +uniffi::include_scaffolding!("glclient"); + +mod runtime; +mod error; +use error::Error; + +mod raw_client; +use raw_client::RawClient; + +mod scheduler; +use scheduler::Scheduler; diff --git a/libs/gl-client-bindings/src/raw_client.rs b/libs/gl-client-bindings/src/raw_client.rs new file mode 100644 index 000000000..80714b1c8 --- /dev/null +++ b/libs/gl-client-bindings/src/raw_client.rs @@ -0,0 +1,42 @@ +use crate::{runtime::exec, Error}; +use anyhow::Context; +use gl_client::node::GClient; +use std::str::FromStr; + +pub struct RawClient { + stub: GClient, +} +impl RawClient { + pub fn new( + node_id: Vec, + network: String, + cert_pem: Vec, + key_pem: Vec, + node_uri: String, + ) -> Result { + let tls = gl_client::tls::TlsConfig::new()?.identity(cert_pem, key_pem); + let network = gl_client::bitcoin::Network::from_str(&network) + .map_err(|e| Error::InvalidArgument(Box::new(e)))?; + + let stub = exec(gl_client::node::Node::new(node_id, network, tls).connect(node_uri)) + .context("error connecting")?; + Ok(Self { stub }) + } + + /// Used by the [crate::scheduler::Scheduler] to use the + /// [gl_client::scheduler::Scheduler::schedule] method to start + /// the node, and then wrap it in the binding representation. + pub(crate) fn with_node(stub: GClient) -> Self { + RawClient { stub } + } + + pub fn call(&self, method: String, payload: Vec) -> Result, Error> { + let mut stub = self.stub.clone(); + crate::runtime::exec(stub.call(&method, payload)) + .map_err(|e| Error::Call { + method, + error: e.to_string(), + }) + .map(|r| r.into_inner().to_vec()) + } +} diff --git a/libs/gl-client-bindings/src/runtime.rs b/libs/gl-client-bindings/src/runtime.rs new file mode 100644 index 000000000..1330e5dde --- /dev/null +++ b/libs/gl-client-bindings/src/runtime.rs @@ -0,0 +1,21 @@ +use tokio::runtime::{Builder, Runtime}; +use std::future::Future; +use once_cell::sync::OnceCell; + +static TOKIO_RUNTIME: OnceCell = OnceCell::new(); + +pub(crate) fn get_runtime<'a>() -> &'a Runtime { + TOKIO_RUNTIME.get_or_init(|| { + let mut builder = Builder::new_multi_thread(); + builder.enable_all(); + builder.build().expect("Unable to build Tokio runtime") + }) +} + +pub(crate) fn exec(f: F) -> T +where + F: Future + Sized + Send, + T: Send, +{ + get_runtime().block_on(f) +} diff --git a/libs/gl-client-bindings/src/scheduler.rs b/libs/gl-client-bindings/src/scheduler.rs new file mode 100644 index 000000000..6e625d916 --- /dev/null +++ b/libs/gl-client-bindings/src/scheduler.rs @@ -0,0 +1,43 @@ +use crate::runtime::exec; +use crate::{Error, RawClient}; +use anyhow::Context; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; + +pub struct Scheduler { + inner: gl_client::scheduler::Scheduler, + tls: Arc>, +} + +impl Scheduler { + pub fn new(node_id: Vec, network: String) -> Result { + let network = gl_client::bitcoin::Network::from_str(&network) + .map_err(|e| Error::InvalidArgument(Box::new(e)))?; + let tls = Arc::new(Mutex::new( + gl_client::tls::TlsConfig::new().context("creating TlsConfig")?, + )); + + let inner = exec(gl_client::scheduler::Scheduler::new(node_id, network)) + .context("creating scheduler")?; + + Ok(Scheduler { inner, tls }) + } + pub fn authenticate(&self, cert_pem: Vec, key_pem: Vec) -> Result<(), Error> { + let tls = gl_client::tls::TlsConfig::new() + .unwrap() + .identity(cert_pem, key_pem); + *self.tls.lock().unwrap() = tls; + Ok(()) + } + + pub fn schedule_raw(&self) -> Result { + let tls = self.tls.lock().unwrap().clone(); + + Ok(RawClient::with_node( + crate::runtime::exec(self.inner.schedule(tls)).map_err(|e| Error::Call { + method: "scheduler.Scheduler/schedule".into(), + error: e.to_string(), + })?, + )) + } +}