gl-sdk 0.4.0

High-level SDK for Greenlight with UniFFI language bindings
Documentation
use crate::{Credentials, Error};
use bip39::Mnemonic;
use std::str::FromStr;
use tracing;

#[derive(uniffi::Object, Clone)]
pub struct Signer {
    seed: Vec<u8>,
    pub(crate) inner: gl_client::signer::Signer,
    credentials: Option<Credentials>,
}

#[uniffi::export]
impl Signer {
    #[uniffi::constructor()]
    pub fn new(phrase: String) -> Result<Signer, Error> {
        let phrase = Mnemonic::from_str(phrase.as_str()).map_err(|_e| Error::phrase_corrupted())?;
        let seed = phrase.to_seed_normalized(&"").to_vec();
        Self::new_from_seed(seed)
    }

    #[uniffi::constructor()]
    pub fn new_from_seed(seed: Vec<u8>) -> Result<Signer, Error> {
        let credentials = gl_client::credentials::Nobody::new();

        let inner = gl_client::signer::Signer::new(
            seed.clone(),
            gl_client::bitcoin::Network::Bitcoin,
            credentials,
        )
        .map_err(|e| Error::other(e.to_string()))?;
        let credentials = None;
        Ok(Signer {
            seed,
            inner,
            credentials,
        })
    }

    pub fn authenticate(&self, creds: &Credentials) -> Result<Signer, Error> {
        let credentials = Some(creds.clone());

        let inner = gl_client::signer::Signer::new(
            self.seed.clone(),
            gl_client::bitcoin::Network::Bitcoin,
            creds.inner.clone(),
        )
        .map_err(|e| Error::other(e.to_string()))?;

        Ok(Signer {
            inner,
            credentials,
            ..self.clone()
        })
    }

    pub fn start(&self) -> Result<Handle, Error> {
        Ok(Handle::spawn(self.inner.clone()))
    }

    pub fn node_id(&self) -> Vec<u8> {
        self.inner.node_id()
    }
}

// Not exported through uniffi, internal logic only.
impl Signer {
    async fn run(&self, signal: tokio::sync::mpsc::Receiver<()>) {
        self.inner
            .run_forever(signal)
            .await
            .expect("Error running signer loop");
    }
}

/// A handle to interact with a signer loop running and processing
/// requests in the background. Used primarily to stop the loop and
/// exiting the signer.
#[derive(uniffi::Object, Clone)]
pub struct Handle {
    chan: tokio::sync::mpsc::Sender<()>,
}

#[uniffi::export]
impl Handle {
    pub fn stop(&self) {
        self.chan.try_send(()).expect("sending shutdown signal");
    }
}

impl Handle {
    /// Spawns the signer on the shared signer runtime and returns a handle to stop it.
    pub(crate) fn spawn(signer: gl_client::signer::Signer) -> Self {
        let (tx, rx) = tokio::sync::mpsc::channel(1);
        crate::util::get_signer_runtime().spawn(async move {
            if let Err(e) = signer.run_forever(rx).await {
                tracing::error!("Error running signer: {e}");
            }
        });
        Self { chan: tx }
    }

    /// Sends the shutdown signal, returning true if the signal was
    /// delivered and false if the signer has already stopped.
    pub(crate) fn try_stop(&self) -> bool {
        self.chan.try_send(()).is_ok()
    }
}