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