vls_frontend/external_persist/
lss.rs

1use super::{Error, ExternalPersist, Info};
2use async_trait::async_trait;
3use bitcoin::PublicKey;
4use lightning_signer::bitcoin;
5use lightning_signer::persist::Mutations;
6use lightning_storage_server::client::{Auth as LssAuth, Client as LssClient, ClientError};
7use lightning_storage_server::Value as LssValue;
8use log::error;
9use tokio::sync::Mutex;
10
11impl From<ClientError> for Error {
12    fn from(e: ClientError) -> Self {
13        match e {
14            ClientError::Connect(e) => {
15                error!("LSS connect error: {}", e);
16                Error::NotAvailable
17            }
18            ClientError::Tonic(e) => {
19                error!("LSS transport error: {}", e);
20                Error::NotAvailable
21            }
22            ClientError::InvalidResponse => {
23                error!("LSS invalid response");
24                Error::NotAvailable
25            }
26            ClientError::InvalidHmac(key, value) => {
27                error!("LSS invalid HMAC for key {} version {}", key, value);
28                Error::NotAuthorized
29            }
30            ClientError::InvalidServerHmac() => {
31                error!("LSS invalid server HMAC");
32                Error::NotAuthorized
33            }
34            ClientError::PutConflict(c) => {
35                error!("LSS put conflict: {:?}", c);
36                Error::Conflicts(c.into_iter().map(|(k, v)| (k, v.version as u64)).collect())
37            }
38        }
39    }
40}
41
42/// An external persistence implementation using lightning-storage-server.
43pub struct Client {
44    client: Mutex<LssClient>,
45    server_public_key: PublicKey,
46    uri: String,
47}
48
49impl Client {
50    /// Get the server's public key.
51    ///
52    /// This will be needed later when calling `new`.  It is also used in the
53    /// signer to create a shared secret with the server.
54    ///
55    /// In a production system, this should be verified and cached.
56    pub async fn get_server_pubkey(uri: &str) -> Result<PublicKey, Error> {
57        let pubkey = LssClient::get_info(uri).await?.0;
58        Ok(PublicKey::new(pubkey))
59    }
60
61    /// Create a new client.
62    ///
63    /// `uri` is the URI of the server.
64    /// `server_public_key` is the server's public key as previously obtained via `get_server_pubkey`.
65    /// `auth` is the authentication token to use.  It includes the client public key.
66    ///
67    /// Note that the client public key is used to locate the client's data on the server.
68    pub async fn new(
69        uri: &str,
70        server_public_key: &PublicKey,
71        auth: LssAuth,
72    ) -> Result<Self, Error> {
73        let (pubkey, _version) = LssClient::get_info(uri).await?;
74
75        assert_eq!(pubkey, server_public_key.inner, "server public key mismatch");
76        let client = LssClient::new(uri, auth).await?;
77        Ok(Self {
78            client: Mutex::new(client),
79            server_public_key: server_public_key.clone(),
80            uri: uri.to_string(),
81        })
82    }
83}
84
85#[async_trait]
86impl ExternalPersist for Client {
87    async fn put(&self, mutations: Mutations, client_hmac: &[u8]) -> Result<Vec<u8>, Error> {
88        let mut client = self.client.lock().await;
89        let kvs = mutations
90            .into_iter()
91            .map(|(k, (version, value))| (k, LssValue { version: version as i64, value }))
92            .collect();
93        let server_hmac = client.put(kvs, client_hmac).await?;
94        Ok(server_hmac)
95    }
96
97    async fn get(&self, key_prefix: String, nonce: &[u8]) -> Result<(Mutations, Vec<u8>), Error> {
98        let mut client = self.client.lock().await;
99        let (kvs, received_hmac) = client.get(key_prefix, nonce).await?;
100        let mutations = Mutations::from_vec(
101            kvs.into_iter().map(|(k, v)| (k, (v.version as u64, v.value))).collect(),
102        );
103        Ok((mutations, received_hmac))
104    }
105
106    async fn info(&self) -> Result<Info, Error> {
107        let (server_public_key, version) = LssClient::get_info(&self.uri).await?;
108        assert_eq!(self.server_public_key.inner, server_public_key, "server public key mismatch");
109
110        Ok(Info { version, pubkey: server_public_key.clone() })
111    }
112}