openzeppelin_relayer/services/signer/stellar/
mod.rs

1// openzeppelin-relayer/src/services/signer/stellar/mod.rs
2//! Stellar signer implementation (local keystore, Google Cloud KMS and Turnkey)
3
4mod google_cloud_kms_signer;
5mod local_signer;
6mod turnkey_signer;
7mod vault_signer;
8
9use async_trait::async_trait;
10use google_cloud_kms_signer::*;
11use local_signer::*;
12use turnkey_signer::*;
13use vault_signer::*;
14
15use crate::{
16    domain::{SignDataRequest, SignDataResponse, SignTransactionResponse, SignTypedDataRequest},
17    models::{
18        Address, NetworkTransactionData, Signer as SignerDomainModel, SignerConfig,
19        SignerRepoModel, SignerType, TransactionRepoModel, VaultSignerConfig,
20    },
21    services::{
22        signer::{SignXdrTransactionResponseStellar, Signer, SignerError, SignerFactoryError},
23        GoogleCloudKmsService, TurnkeyService, VaultConfig, VaultService,
24    },
25};
26
27use super::DataSignerTrait;
28
29#[cfg(test)]
30use mockall::automock;
31
32#[cfg_attr(test, automock)]
33/// Trait defining Stellar-specific signing operations
34///
35/// This trait extends the basic signing functionality with methods specific
36/// to the Stellar blockchain, following the same pattern as SolanaSignTrait.
37#[async_trait]
38pub trait StellarSignTrait: Sync + Send {
39    /// Signs a Stellar transaction in XDR format
40    ///
41    /// # Arguments
42    ///
43    /// * `unsigned_xdr` - The unsigned transaction in XDR format
44    /// * `network_passphrase` - The network passphrase for the Stellar network
45    ///
46    /// # Returns
47    ///
48    /// A signed transaction response containing the signed XDR and signature
49    async fn sign_xdr_transaction(
50        &self,
51        unsigned_xdr: &str,
52        network_passphrase: &str,
53    ) -> Result<SignXdrTransactionResponseStellar, SignerError>;
54}
55
56pub enum StellarSigner {
57    Local(Box<LocalSigner>),
58    Vault(VaultSigner<VaultService>),
59    GoogleCloudKms(GoogleCloudKmsSigner),
60    Turnkey(TurnkeySigner),
61}
62
63#[async_trait]
64impl Signer for StellarSigner {
65    async fn address(&self) -> Result<Address, SignerError> {
66        match self {
67            Self::Local(s) => s.address().await,
68            Self::Vault(s) => s.address().await,
69            Self::GoogleCloudKms(s) => s.address().await,
70            Self::Turnkey(s) => s.address().await,
71        }
72    }
73
74    async fn sign_transaction(
75        &self,
76        tx: NetworkTransactionData,
77    ) -> Result<SignTransactionResponse, SignerError> {
78        match self {
79            Self::Local(s) => s.sign_transaction(tx).await,
80            Self::Vault(s) => s.sign_transaction(tx).await,
81            Self::GoogleCloudKms(s) => s.sign_transaction(tx).await,
82            Self::Turnkey(s) => s.sign_transaction(tx).await,
83        }
84    }
85}
86
87#[async_trait]
88impl StellarSignTrait for StellarSigner {
89    async fn sign_xdr_transaction(
90        &self,
91        unsigned_xdr: &str,
92        network_passphrase: &str,
93    ) -> Result<SignXdrTransactionResponseStellar, SignerError> {
94        match self {
95            Self::Local(s) => {
96                s.sign_xdr_transaction(unsigned_xdr, network_passphrase)
97                    .await
98            }
99            Self::Vault(s) => {
100                s.sign_xdr_transaction(unsigned_xdr, network_passphrase)
101                    .await
102            }
103            Self::GoogleCloudKms(s) => {
104                s.sign_xdr_transaction(unsigned_xdr, network_passphrase)
105                    .await
106            }
107            Self::Turnkey(s) => {
108                s.sign_xdr_transaction(unsigned_xdr, network_passphrase)
109                    .await
110            }
111        }
112    }
113}
114
115pub struct StellarSignerFactory;
116
117impl StellarSignerFactory {
118    pub fn create_stellar_signer(
119        m: &SignerDomainModel,
120    ) -> Result<StellarSigner, SignerFactoryError> {
121        let signer = match &m.config {
122            SignerConfig::Local(_) => {
123                let local_signer = LocalSigner::new(m)?;
124                StellarSigner::Local(Box::new(local_signer))
125            }
126            SignerConfig::Vault(config) => {
127                let vault_config = VaultConfig::new(
128                    config.address.clone(),
129                    config.role_id.clone(),
130                    config.secret_id.clone(),
131                    config.namespace.clone(),
132                    config
133                        .mount_point
134                        .clone()
135                        .unwrap_or_else(|| "secret".to_string()),
136                    None,
137                );
138                let vault_service = VaultService::new(vault_config);
139
140                StellarSigner::Vault(VaultSigner::new(
141                    m.id.clone(),
142                    config.clone(),
143                    vault_service,
144                ))
145            }
146            SignerConfig::GoogleCloudKms(config) => {
147                let service = GoogleCloudKmsService::new(config)
148                    .map_err(|e| SignerFactoryError::CreationFailed(e.to_string()))?;
149                StellarSigner::GoogleCloudKms(GoogleCloudKmsSigner::new(service))
150            }
151            SignerConfig::Turnkey(config) => {
152                let service = TurnkeyService::new(config.clone())
153                    .map_err(|e| SignerFactoryError::CreationFailed(e.to_string()))?;
154                StellarSigner::Turnkey(TurnkeySigner::new(service))
155            }
156            SignerConfig::AwsKms(_) => {
157                return Err(SignerFactoryError::UnsupportedType("AWS KMS".into()))
158            }
159            SignerConfig::VaultTransit(_) => {
160                return Err(SignerFactoryError::UnsupportedType("Vault Transit".into()))
161            }
162            SignerConfig::Cdp(_) => return Err(SignerFactoryError::UnsupportedType("CDP".into())),
163        };
164        Ok(signer)
165    }
166}