Skip to main content

glsdk/
node_builder.rs

1// Builder-style Node creation.
2//
3// `NodeBuilder` is the sole public entry point for Node construction
4// across all foreign bindings. The shape is "request the action,
5// optionally with modifiers":
6//
7//     // Signerless connect — caller does not have the mnemonic in
8//     // this process. The SDK runs no signer; signing happens
9//     // elsewhere (paired device, CLN node's local signer, hardware
10//     // signer). This is the supported model for keyless clients.
11//     let node = NodeBuilder::new(&config).connect(credentials, None)?;
12//
13//     // Signed connect — caller hands the mnemonic per build call,
14//     // SDK spawns a signer.
15//     let node = NodeBuilder::new(&config).connect(credentials, Some(mnemonic))?;
16//
17//     // Register / recover require a mnemonic by definition (the
18//     // signer must sign the registration/recovery challenge).
19//     let node = NodeBuilder::new(&config)
20//         .with_event_listener(listener)
21//         .register(mnemonic, invite_code)?;
22//
23// The mnemonic is the security-sensitive input: hand it in only when
24// you actually want the SDK to act as a signer for that call.
25//
26// Adding a new modifier later is a new `with_*` setter — additive,
27// never breaks existing callers.
28
29use std::sync::Arc;
30
31use crate::{
32    config::Config,
33    node::{Node, NodeEventListener},
34    Error,
35};
36
37/// Configurable Node construction. See module docs.
38///
39/// All fields are immutable after construction. Each `with_*` setter
40/// returns a fresh `Arc<NodeBuilder>` that shares ownership of any
41/// previously-installed modifiers via `Arc<dyn …>`. No interior
42/// mutability, no locks — the builder is a value, not a state
43/// machine.
44#[derive(uniffi::Object)]
45pub struct NodeBuilder {
46    config: Arc<Config>,
47    event_listener: Option<Arc<dyn NodeEventListener>>,
48}
49
50#[uniffi::export]
51impl NodeBuilder {
52    /// Create a builder for a Node with `config`. No I/O happens
53    /// until you call `connect` / `register` / `recover` /
54    /// `register_or_recover`.
55    #[uniffi::constructor]
56    pub fn new(config: &Config) -> Arc<Self> {
57        Arc::new(Self {
58            config: Arc::new(config.clone()),
59            event_listener: None,
60        })
61    }
62
63    /// Install a node event listener. Events fire from the moment the
64    /// gRPC stream is established by the build call (`register` /
65    /// `recover` / `connect` / …), so attach the listener via the
66    /// builder rather than after the fact to capture events from the
67    /// very first moment.
68    ///
69    /// Returns a new builder that shares the rest of the
70    /// configuration. Build calls on the returned builder will
71    /// install the listener; the original builder is unchanged.
72    pub fn with_event_listener(
73        self: Arc<Self>,
74        listener: Box<dyn NodeEventListener>,
75    ) -> Arc<Self> {
76        // UniFFI's callback-interface lowering hands us a
77        // `Box<dyn Trait>`. We re-wrap it as `Arc<dyn Trait>` because
78        // the builder is reusable across multiple build calls — each
79        // build clones the Arc into the resulting Node, and `Box`
80        // can't be cloned. This is a one-time cost paid per setter
81        // call.
82        Arc::new(Self {
83            config: Arc::clone(&self.config),
84            event_listener: Some(Arc::from(listener)),
85        })
86    }
87
88    /// Register a new Greenlight node and return a connected Node
89    /// with the SDK signer running and any configured modifiers
90    /// applied.
91    ///
92    /// `mnemonic` is required — registration drives the signer to
93    /// sign the registration challenge, so the SDK must hold the
94    /// seed for this call.
95    pub fn register(
96        &self,
97        mnemonic: String,
98        invite_code: Option<String>,
99    ) -> Result<Arc<Node>, Error> {
100        let node = crate::register_internal(mnemonic, invite_code, &self.config)?;
101        self.attach_observers(&node)?;
102        Ok(node)
103    }
104
105    /// Recover credentials for an existing node and return a
106    /// connected Node with any configured modifiers applied.
107    ///
108    /// `mnemonic` is required — recovery drives the signer to
109    /// authenticate.
110    pub fn recover(&self, mnemonic: String) -> Result<Arc<Node>, Error> {
111        let node = crate::recover_internal(mnemonic, &self.config)?;
112        self.attach_observers(&node)?;
113        Ok(node)
114    }
115
116    /// Connect to an existing node using saved credentials and return
117    /// a connected Node with any configured modifiers applied.
118    ///
119    /// If `mnemonic` is `Some(...)`, the SDK spawns a signer for the
120    /// connected Node. If `None`, the Node is signerless and signing
121    /// happens elsewhere (paired device, CLN node's local signer,
122    /// hardware signer).
123    pub fn connect(
124        &self,
125        credentials: Vec<u8>,
126        mnemonic: Option<String>,
127    ) -> Result<Arc<Node>, Error> {
128        let node = match mnemonic {
129            Some(mnemonic) => crate::connect_internal(mnemonic, credentials, &self.config)?,
130            None => crate::connect_signerless_internal(credentials, &self.config)?,
131        };
132        self.attach_observers(&node)?;
133        Ok(node)
134    }
135
136    /// Try to recover; if the node doesn't exist, register a new one.
137    ///
138    /// `mnemonic` is required — both recover and register drive the
139    /// signer.
140    pub fn register_or_recover(
141        &self,
142        mnemonic: String,
143        invite_code: Option<String>,
144    ) -> Result<Arc<Node>, Error> {
145        let node =
146            crate::register_or_recover_internal(mnemonic, invite_code, &self.config)?;
147        self.attach_observers(&node)?;
148        Ok(node)
149    }
150}
151
152impl NodeBuilder {
153    /// Attach all configured modifiers to a freshly-built Node.
154    /// Modifiers are shared (not consumed) — the same builder can
155    /// drive multiple builds and they all get the same listener.
156    fn attach_observers(&self, node: &Arc<Node>) -> Result<(), Error> {
157        if let Some(listener) = self.event_listener.as_ref() {
158            node.set_event_listener(Arc::clone(listener))?;
159        }
160        Ok(())
161    }
162}