miden_multisig_client/
builder.rs1use std::path::PathBuf;
4use std::sync::Arc;
5
6use miden_client::DebugMode;
7use miden_client::builder::ClientBuilder;
8use miden_client::keystore::FilesystemKeyStore;
9use miden_client::rpc::Endpoint;
10use miden_client_sqlite_store::SqliteStore;
11use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SigningKey as EcdsaSecretKey;
12use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey;
13use miden_protocol::crypto::rand::RandomCoin;
14
15use crate::MidenSdkClient;
16use crate::client::MultisigClient;
17use crate::error::{MultisigError, Result};
18use crate::keystore::{EcdsaGuardianKeyStore, GuardianKeyStore, KeyManager};
19
20fn configured_client_builder(endpoint: &Endpoint) -> ClientBuilder<FilesystemKeyStore> {
21 if endpoint == &Endpoint::devnet() {
22 ClientBuilder::<FilesystemKeyStore>::for_devnet()
23 } else if endpoint == &Endpoint::testnet() {
24 ClientBuilder::<FilesystemKeyStore>::for_testnet()
25 } else if endpoint == &Endpoint::localhost() {
26 ClientBuilder::<FilesystemKeyStore>::for_localhost()
27 } else {
28 ClientBuilder::<FilesystemKeyStore>::new().grpc_client(endpoint, Some(20_000))
29 }
30}
31
32pub struct MultisigClientBuilder {
49 miden_endpoint: Option<Endpoint>,
50 guardian_endpoint: Option<String>,
51 account_dir: Option<PathBuf>,
52 key_manager: Option<Arc<dyn KeyManager>>,
53}
54
55impl Default for MultisigClientBuilder {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl MultisigClientBuilder {
62 pub fn new() -> Self {
64 Self {
65 miden_endpoint: None,
66 guardian_endpoint: None,
67 account_dir: None,
68 key_manager: None,
69 }
70 }
71
72 pub fn miden_endpoint(mut self, endpoint: Endpoint) -> Self {
74 self.miden_endpoint = Some(endpoint);
75 self
76 }
77
78 pub fn guardian_endpoint(mut self, endpoint: impl Into<String>) -> Self {
80 self.guardian_endpoint = Some(endpoint.into());
81 self
82 }
83
84 pub fn account_dir(mut self, path: impl Into<PathBuf>) -> Self {
88 self.account_dir = Some(path.into());
89 self
90 }
91
92 pub fn key_manager(mut self, key_manager: Box<dyn KeyManager>) -> Self {
94 self.key_manager = Some(key_manager.into());
95 self
96 }
97
98 pub fn with_secret_key(mut self, secret_key: SecretKey) -> Self {
100 self.key_manager = Some(Arc::new(GuardianKeyStore::new(secret_key)));
101 self
102 }
103
104 pub fn with_ecdsa_secret_key(mut self, secret_key: EcdsaSecretKey) -> Self {
106 self.key_manager = Some(Arc::new(EcdsaGuardianKeyStore::new(secret_key)));
107 self
108 }
109
110 pub fn generate_key(mut self) -> Self {
112 self.key_manager = Some(Arc::new(GuardianKeyStore::generate()));
113 self
114 }
115
116 pub fn generate_ecdsa_key(mut self) -> Self {
118 self.key_manager = Some(Arc::new(EcdsaGuardianKeyStore::generate()));
119 self
120 }
121
122 pub async fn build(self) -> Result<MultisigClient> {
124 let miden_endpoint = self
125 .miden_endpoint
126 .ok_or_else(|| MultisigError::MissingConfig("miden_endpoint".to_string()))?;
127
128 let guardian_endpoint = self
129 .guardian_endpoint
130 .ok_or_else(|| MultisigError::MissingConfig("guardian_endpoint".to_string()))?;
131
132 let account_dir = self
133 .account_dir
134 .ok_or_else(|| MultisigError::MissingConfig("account_dir".to_string()))?;
135
136 let key_manager = self.key_manager.ok_or(MultisigError::NoSigner)?;
137
138 std::fs::create_dir_all(&account_dir).map_err(|e| {
140 MultisigError::MidenClient(format!("failed to create account dir: {}", e))
141 })?;
142
143 let miden_client = create_miden_client(&account_dir, &miden_endpoint).await?;
144
145 Ok(MultisigClient::new(
146 miden_client,
147 key_manager,
148 guardian_endpoint,
149 account_dir,
150 miden_endpoint,
151 ))
152 }
153}
154
155pub(crate) async fn create_miden_client(
160 account_dir: &std::path::Path,
161 endpoint: &Endpoint,
162) -> Result<MidenSdkClient> {
163 let timestamp = std::time::SystemTime::now()
164 .duration_since(std::time::UNIX_EPOCH)
165 .unwrap_or_default()
166 .as_millis();
167 let random_suffix: u32 = rand::random();
168 let store_path = account_dir.join(format!(
169 "miden-client-{}-{}.sqlite",
170 timestamp, random_suffix
171 ));
172 let store = SqliteStore::new(store_path)
173 .await
174 .map_err(|e| MultisigError::MidenClient(format!("failed to open SQLite store: {}", e)))?;
175 let store = Arc::new(store);
176
177 let rng_seed: [u32; 4] = rand::random();
178 let rng = Box::new(RandomCoin::new(rng_seed.into()));
179
180 configured_client_builder(endpoint)
181 .store(store)
182 .rng(rng)
183 .in_debug_mode(DebugMode::Enabled)
184 .tx_discard_delta(Some(20))
185 .max_block_number_delta(256)
186 .build()
187 .await
188 .map_err(|e| MultisigError::MidenClient(format!("failed to create miden client: {}", e)))
189}