1use crate::{
7 ExtendedPrivateKey, ExtendedPublicKey, KeygenId, KeygenPrivateKey, PublicKey, RoomUUID, Scheme, SecretShare, SodotError,
8 ffi::{self, AsRawFFI, ConstSlice, ConstStr, FromFFI, HeapString, Room, ToFFI, async_ffi_double, async_ffi_single},
9};
10use std::num::NonZeroU16;
11
12pub type Bip340SecretShare = SecretShare<Bip340>;
14
15pub type Bip340PublicKey = PublicKey<Bip340, 32>;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Bip340Tweak(pub(crate) [u8; 32]);
21
22impl Bip340Tweak {
23 pub fn as_bytes(&self) -> &[u8; 32] {
25 &self.0
26 }
27}
28
29impl From<[u8; 32]> for Bip340Tweak {
30 fn from(bytes: [u8; 32]) -> Self {
31 Bip340Tweak(bytes)
32 }
33}
34
35pub type Bip340ExtendedPublicKey = ExtendedPublicKey<Bip340>;
37
38pub type Bip340ExtendedPrivateKey = ExtendedPrivateKey<Bip340>;
40
41impl Bip340PublicKey {
42 pub fn as_bytes(&self) -> &[u8; 32] {
44 &self.0
45 }
46}
47
48impl From<[u8; 32]> for Bip340PublicKey {
49 fn from(k: [u8; 32]) -> Self {
50 Self(k, std::marker::PhantomData)
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct Bip340 {
60 host_url: String,
61}
62
63impl Scheme for Bip340 {
64 fn new(host_url: String) -> Self {
65 Self { host_url }
66 }
67
68 fn host_url(&self) -> &str {
69 &self.host_url
70 }
71}
72
73impl Bip340 {
74 pub fn new(host_url: String) -> Self {
76 <Self as Scheme>::new(host_url)
77 }
78
79 pub fn with_default_host() -> Self {
81 <Self as Scheme>::with_default_host()
82 }
83
84 pub fn init_keygen(&self) -> Result<(KeygenId, KeygenPrivateKey), SodotError> {
86 <Self as Scheme>::init_keygen(self)
87 }
88
89 pub async fn create_room(&self, num_parties: NonZeroU16, api_key: &str) -> Result<RoomUUID, SodotError> {
91 <Self as Scheme>::create_room(self, num_parties, api_key).await
92 }
93
94 pub async fn keygen(
100 &self,
101 room_uuid: &RoomUUID,
102 num_parties: NonZeroU16,
103 threshold: NonZeroU16,
104 keygen_init_keypair: &KeygenPrivateKey,
105 keygen_ids: &[KeygenId],
106 ) -> Result<(Bip340PublicKey, Bip340SecretShare), SodotError> {
107 let room = Room::new(&self.host_url, room_uuid);
108 let keygen_init_keypair = keygen_init_keypair.ffi();
109
110 let keygen_ids: Vec<_> = keygen_ids.iter().map(|s| ConstStr::from_str(s.as_str())).collect();
111 let keygen_slice = ConstSlice::new(&keygen_ids);
112
113 async_ffi_double(|cb| unsafe {
114 ffi::sodot_bip340_keygen(
115 room.raw_ffi(),
116 num_parties.get(),
117 threshold.get(),
118 keygen_init_keypair.raw_ffi(),
119 keygen_slice.raw_ffi(),
120 cb.callback(),
121 cb.extra(),
122 )
123 })
124 .await
125 }
126
127 pub async fn sign(
132 &self,
133 room_uuid: &RoomUUID,
134 secret_share: &Bip340SecretShare,
135 message: &[u8],
136 derivation_path: &[u32],
137 tweak: Option<&Bip340Tweak>,
138 ) -> Result<[u8; 64], SodotError> {
139 let room = Room::new(&self.host_url, room_uuid);
140 let secret_share_str = secret_share.ffi();
141 let message_str = message.ffi();
142 let derivation_slice = ConstSlice::new(derivation_path);
143
144 let tweak = tweak.ffi();
145
146 async_ffi_single(|cb| unsafe {
147 ffi::sodot_bip340_sign(
148 room.raw_ffi(),
149 secret_share_str.raw_ffi(),
150 message_str.raw_ffi(),
151 derivation_slice.raw_ffi(),
152 tweak.raw_ffi(),
153 cb.callback(),
154 cb.extra(),
155 )
156 })
157 .await
158 }
159
160 pub fn derive_tweak_pubkey(
162 &self,
163 secret_share: &Bip340SecretShare,
164 derivation_path: &[u32],
165 tweak: Option<&Bip340Tweak>,
166 ) -> Result<Bip340PublicKey, SodotError> {
167 let secret_share_str = secret_share.ffi();
168 let derivation_slice = ConstSlice::new(derivation_path);
169
170 let tweak = tweak.ffi();
171
172 let mut pubkey_out = HeapString::NULL;
173 let result = unsafe {
174 ffi::sodot_bip340_derive_tweak_pubkey(
175 secret_share_str.raw_ffi(),
176 derivation_slice.raw_ffi(),
177 tweak.raw_ffi(),
178 pubkey_out.ffi_mut(),
179 )
180 };
181 result.ok_and(|| FromFFI::parse_ffi(pubkey_out.as_str()))
182 }
183
184 pub fn get_xpub(&self, secret_share: &Bip340SecretShare) -> Result<Bip340ExtendedPublicKey, SodotError> {
186 let secret_share_str = secret_share.ffi();
187
188 let mut xpub_out = HeapString::NULL;
189 let result = unsafe { ffi::sodot_bip340_get_xpub(secret_share_str.raw_ffi(), xpub_out.ffi_mut()) };
190 result.ok_and(|| FromFFI::parse_ffi(xpub_out.as_str()))
191 }
192
193 pub fn derive_pubkey_from_xpub(
195 &self,
196 xpub: &Bip340ExtendedPublicKey,
197 derivation_path: &[u32],
198 tweak: Option<&Bip340Tweak>,
199 ) -> Result<Bip340PublicKey, SodotError> {
200 let xpub = xpub.ffi();
201 let derivation_slice = ConstSlice::new(derivation_path);
202
203 let tweak = tweak.ffi();
204
205 let mut pubkey_out = HeapString::NULL;
206 let result = unsafe {
207 ffi::sodot_bip340_derive_pubkey_from_xpub(
208 xpub.raw_ffi(),
209 derivation_slice.raw_ffi(),
210 tweak.raw_ffi(),
211 pubkey_out.ffi_mut(),
212 )
213 };
214 result.ok_and(|| FromFFI::parse_ffi(pubkey_out.as_str()))
215 }
216
217 pub fn derive_private_key_from_xpriv(
219 &self,
220 xpriv: &Bip340ExtendedPrivateKey,
221 derivation_path: &[u32],
222 ) -> Result<[u8; 32], SodotError> {
223 let xpriv = xpriv.ffi();
224 let derivation_slice = ConstSlice::new(derivation_path);
225
226 let mut privkey_out = HeapString::NULL;
227 let result = unsafe {
228 ffi::sodot_bip340_derive_private_key_from_xpriv(xpriv.raw_ffi(), derivation_slice.raw_ffi(), privkey_out.ffi_mut())
229 };
230 result.ok_and(|| FromFFI::parse_ffi(privkey_out.as_str()))
231 }
232
233 pub async fn refresh(
235 &self,
236 room_uuid: &RoomUUID,
237 secret_share: &Bip340SecretShare,
238 ) -> Result<(Bip340PublicKey, Bip340SecretShare), SodotError> {
239 let room = Room::new(&self.host_url, room_uuid);
240 let secret_share_str = secret_share.ffi();
241
242 async_ffi_double(|cb| unsafe {
243 ffi::sodot_bip340_refresh(room.raw_ffi(), secret_share_str.raw_ffi(), cb.callback(), cb.extra())
244 })
245 .await
246 }
247
248 pub fn export_id(&self, secret_share: &Bip340SecretShare) -> Result<KeygenId, SodotError> {
250 let secret_share_str = secret_share.ffi();
251
252 let mut id_out = HeapString::NULL;
253 let result = unsafe { ffi::sodot_bip340_get_export_id(secret_share_str.raw_ffi(), id_out.ffi_mut()) };
254 result.ok_and(|| KeygenId::new(id_out.as_str().to_string()))
255 }
256
257 pub async fn export_full_private_key(
263 &self,
264 room_uuid: &RoomUUID,
265 secret_share: &Bip340SecretShare,
266 export_to: &KeygenId,
267 ) -> Result<Option<Bip340ExtendedPrivateKey>, SodotError> {
268 let room = Room::new(&self.host_url, room_uuid);
269 let secret_share = ConstStr::from_str(secret_share.as_str());
270 let export_to = ConstStr::from_str(export_to.as_str());
271 async_ffi_single(|cb| unsafe {
272 ffi::sodot_bip340_export_full_private_key(
273 room.raw_ffi(),
274 secret_share.raw_ffi(),
275 export_to.raw_ffi(),
276 cb.callback(),
277 cb.extra(),
278 )
279 })
280 .await
281 }
282
283 pub fn offline_export_full_private_key(
285 &self,
286 secret_shares: &[Bip340SecretShare],
287 ) -> Result<Bip340ExtendedPrivateKey, SodotError> {
288 let secret_share_strs: Vec<_> = secret_shares.iter().map(|s| ConstStr::from_str(s.as_str())).collect();
289 let secret_shares_slice = ConstSlice::new(&secret_share_strs);
290
291 let mut privkey_out = HeapString::NULL;
292 let result =
293 unsafe { ffi::sodot_bip340_offline_export_full_private_key(secret_shares_slice.raw_ffi(), privkey_out.ffi_mut()) };
294 result.ok_and(|| FromFFI::parse_ffi(privkey_out.as_str()))
295 }
296
297 pub async fn import_private_key_recipient(
299 &self,
300 room_uuid: &RoomUUID,
301 threshold: NonZeroU16,
302 keygen_init_keypair: &KeygenPrivateKey,
303 keygen_ids: &[KeygenId],
304 ) -> Result<(Bip340PublicKey, Bip340SecretShare), SodotError> {
305 let room = Room::new(&self.host_url, room_uuid);
306 let keygen_init_keypair = keygen_init_keypair.ffi();
307
308 let keygen_ids: Vec<_> = keygen_ids.iter().map(|s| ConstStr::from_str(s.as_str())).collect();
309 let keygen_slice = ConstSlice::new(&keygen_ids);
310
311 async_ffi_double(|cb| unsafe {
312 ffi::sodot_bip340_import_private_key_recipient(
313 room.raw_ffi(),
314 threshold.get(),
315 keygen_init_keypair.raw_ffi(),
316 keygen_slice.raw_ffi(),
317 cb.callback(),
318 cb.extra(),
319 )
320 })
321 .await
322 }
323
324 pub async fn import_private_key_importer(
326 &self,
327 room_uuid: &RoomUUID,
328 threshold: NonZeroU16,
329 private_key: &[u8; 32],
330 keygen_init_keypair: &KeygenPrivateKey,
331 keygen_ids: &[KeygenId],
332 ) -> Result<(Bip340PublicKey, Bip340SecretShare), SodotError> {
333 let room = Room::new(&self.host_url, room_uuid);
334 let private_key_ffi = private_key.ffi();
335 let keygen_init_keypair = keygen_init_keypair.ffi();
336
337 let keygen_ids: Vec<_> = keygen_ids.iter().map(|s| ConstStr::from_str(s.as_str())).collect();
338 let keygen_slice = ConstSlice::new(&keygen_ids);
339
340 async_ffi_double(|cb| unsafe {
341 ffi::sodot_bip340_import_private_key_importer(
342 room.raw_ffi(),
343 threshold.get(),
344 private_key_ffi.raw_ffi(),
345 keygen_init_keypair.raw_ffi(),
346 keygen_slice.raw_ffi(),
347 cb.callback(),
348 cb.extra(),
349 )
350 })
351 .await
352 }
353
354 pub async fn reshare_new_party(
356 &self,
357 room_uuid: &RoomUUID,
358 new_threshold: NonZeroU16,
359 keygen_init_keypair: &KeygenPrivateKey,
360 keygen_ids: &[KeygenId],
361 ) -> Result<(Bip340PublicKey, Bip340SecretShare), SodotError> {
362 let room = Room::new(&self.host_url, room_uuid);
363 let keygen_init_keypair = keygen_init_keypair.ffi();
364
365 let keygen_ids: Vec<_> = keygen_ids.iter().map(|s| ConstStr::from_str(s.as_str())).collect();
366 let keygen_slice = ConstSlice::new(&keygen_ids);
367
368 async_ffi_double(|cb| unsafe {
369 ffi::sodot_bip340_reshare_new_party(
370 room.raw_ffi(),
371 new_threshold.get(),
372 keygen_init_keypair.raw_ffi(),
373 keygen_slice.raw_ffi(),
374 cb.callback(),
375 cb.extra(),
376 )
377 })
378 .await
379 }
380
381 pub async fn reshare_remaining_party(
383 &self,
384 room_uuid: &RoomUUID,
385 new_threshold: NonZeroU16,
386 secret_share: &Bip340SecretShare,
387 keygen_ids: &[KeygenId],
388 ) -> Result<(Bip340PublicKey, Bip340SecretShare), SodotError> {
389 let room = Room::new(&self.host_url, room_uuid);
390 let secret_share_str = secret_share.ffi();
391
392 let keygen_ids: Vec<_> = keygen_ids.iter().map(|s| ConstStr::from_str(s.as_str())).collect();
393 let keygen_slice = ConstSlice::new(&keygen_ids);
394
395 async_ffi_double(|cb| unsafe {
396 ffi::sodot_bip340_reshare_remaining_party(
397 room.raw_ffi(),
398 new_threshold.get(),
399 secret_share_str.raw_ffi(),
400 keygen_slice.raw_ffi(),
401 cb.callback(),
402 cb.extra(),
403 )
404 })
405 .await
406 }
407}