1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/* Copyright 2023 Architect Financial Technologies LLC. This is free
 * software released under the GNU Affero Public License version 3. */
//! secrets database

use crate::config::Common;
use crate::protocol::secrets::SecretKey;
use anyhow::{bail, Result};
use bytes::Bytes;
use netidx::{protocol::value::Value, utils::pack};
use netidx_protocols::{call_rpc, rpc::client::Proc};
use serde::de::DeserializeOwned;

/// The secrets db is where exchange and wallet keys are stored. They
/// are encrypted with your private key, and may decrypted as needed
/// by components with valid credentials.
pub struct SecretsQueryApi {
    add_secret: Proc,
    delete_secret: Proc,
    list_secrets: Proc,
    get_secret: Proc,
}

impl SecretsQueryApi {
    /// Create a new secrets api client
    pub async fn new(common: &Common) -> Result<Self> {
        let base = common.paths.core_secrets();
        let subscriber = &common.subscriber;
        Ok(Self {
            add_secret: Proc::new(subscriber, base.append("add-secret")).await?,
            delete_secret: Proc::new(subscriber, base.append("delete-secret")).await?,
            list_secrets: Proc::new(subscriber, base.append("list-secrets")).await?,
            get_secret: Proc::new(subscriber, base.append("get-secret")).await?,
        })
    }

    /// Add a secret to the secrets db
    pub async fn add_secret(&self, secret_key: SecretKey, value: &[u8]) -> Result<()> {
        let value = Value::Bytes(Bytes::copy_from_slice(value));
        let res =
            call_rpc!(&self.add_secret, secret_key: secret_key, value: value).await?;
        match res {
            Value::Error(e) => bail!(e.to_string()),
            Value::Ok => Ok(()),
            _ => bail!("unexpected response"),
        }
    }

    /// Remove a secret from the secrets db
    pub async fn delete_secret(&self, secret_key: SecretKey) -> Result<()> {
        let res = call_rpc!(&self.delete_secret, secret_key: secret_key).await?;
        match res {
            Value::Error(e) => bail!(e.to_string()),
            Value::Ok => Ok(()),
            _ => bail!("unexpected response"),
        }
    }

    /// List the secrets present in the db
    pub async fn list_secrets(&self) -> Result<Vec<SecretKey>> {
        // CR estokes for estokes: Make calling zero argument rpcs more ergonomic
        let args: [(&'static str, Value); 0] = [];
        let res = self.list_secrets.call(args).await?;
        match res {
            Value::Error(e) => bail!(e.to_string()),
            v => Ok(v.cast_to()?),
        }
    }

    /// Get the secret associated with the specified key
    pub async fn get_secret(&self, secret_key: SecretKey) -> Result<Option<Vec<u8>>> {
        let secret_key = Value::Bytes(pack(&secret_key)?.freeze());
        let res = call_rpc!(&self.get_secret, secret_key: secret_key).await?;
        match res {
            Value::Error(e) => bail!(e.to_string()),
            v => Ok(v.cast_to()?),
        }
    }

    /// Get the secret needed to connect to the specified venue
    pub async fn get_any_exchangekey_secret_for_venue<T: DeserializeOwned>(
        &self,
        venue: &crate::protocol::symbology::Venue,
    ) -> Result<T> {
        match self.list_secrets().await?.into_iter().find(|secret_key| match secret_key {
            SecretKey::ExchangeKey(_label, venue1, _account) => *venue1 == *venue,
            SecretKey::EVMKey(_, _) => false,
        }) {
            None => bail!("no exchange key found for {:?}", venue),
            Some(secret_key) => {
                let creds_json = self.get_secret(secret_key).await?.unwrap();
                let creds: T = serde_json::from_str(std::str::from_utf8(&creds_json)?)?;
                Ok(creds)
            }
        }
    }
}