1use serde::Deserialize;
2
3use crate::crypto::MultisigSignature;
4use crate::kmd::requests::*;
5use crate::kmd::responses::*;
6use crate::transaction::Transaction;
7use crate::{Ed25519PublicKey, Error, MasterDerivationKey};
8
9const KMD_TOKEN_HEADER: &str = "X-KMD-API-Token";
10
11pub struct KmdClient {
13 address: String,
14 token: String,
15 http_client: reqwest::Client,
16}
17
18impl KmdClient {
19 pub fn new(address: &str, token: &str) -> KmdClient {
20 KmdClient {
21 address: address.to_string(),
22 token: token.to_string(),
23 http_client: reqwest::Client::new(),
24 }
25 }
26
27 pub fn versions(&self) -> Result<VersionsResponse, Error> {
29 self.do_v1_request(VersionsRequest)
30 }
31
32 pub fn list_wallets(&self) -> Result<ListWalletsResponse, Error> {
34 self.do_v1_request(ListWalletsRequest)
35 }
36
37 pub fn create_wallet(
39 &self,
40 wallet_name: &str,
41 wallet_password: &str,
42 wallet_driver_name: &str,
43 master_derivation_key: MasterDerivationKey,
44 ) -> Result<CreateWalletResponse, Error> {
45 let req = CreateWalletRequest {
46 master_derivation_key,
47 wallet_driver_name: wallet_driver_name.to_string(),
48 wallet_name: wallet_name.to_string(),
49 wallet_password: wallet_password.to_string(),
50 };
51 self.do_v1_request(req)
52 }
53
54 pub fn init_wallet_handle(
60 &self,
61 wallet_id: &str,
62 wallet_password: &str,
63 ) -> Result<InitWalletHandleResponse, Error> {
64 let req = InitWalletHandleRequest {
65 wallet_id: wallet_id.to_string(),
66 wallet_password: wallet_password.to_string(),
67 };
68 self.do_v1_request(req)
69 }
70
71 pub fn release_wallet_handle(
73 &self,
74 wallet_handle: &str,
75 ) -> Result<ReleaseWalletHandleResponse, Error> {
76 let req = ReleaseWalletHandleRequest {
77 wallet_handle_token: wallet_handle.to_string(),
78 };
79 self.do_v1_request(req)
80 }
81
82 pub fn renew_wallet_handle(
84 &self,
85 wallet_handle: &str,
86 ) -> Result<RenewWalletHandleResponse, Error> {
87 let req = RenewWalletHandleRequest {
88 wallet_handle_token: wallet_handle.to_string(),
89 };
90 self.do_v1_request(req)
91 }
92
93 pub fn rename_wallet(
95 &self,
96 wallet_id: &str,
97 wallet_password: &str,
98 new_name: &str,
99 ) -> Result<RenameWalletResponse, Error> {
100 let req = RenameWalletRequest {
101 wallet_id: wallet_id.to_string(),
102 wallet_password: wallet_password.to_string(),
103 wallet_name: new_name.to_string(),
104 };
105 self.do_v1_request(req)
106 }
107
108 pub fn get_wallet(&self, wallet_handle: &str) -> Result<GetWalletResponse, Error> {
110 let req = GetWalletRequest {
111 wallet_handle_token: wallet_handle.to_string(),
112 };
113 self.do_v1_request(req)
114 }
115
116 pub fn export_master_derivation_key(
118 &self,
119 wallet_handle: &str,
120 wallet_password: &str,
121 ) -> Result<ExportMasterDerivationKeyResponse, Error> {
122 let req = ExportMasterDerivationKeyRequest {
123 wallet_handle_token: wallet_handle.to_string(),
124 wallet_password: wallet_password.to_string(),
125 };
126 self.do_v1_request(req)
127 }
128
129 pub fn import_key(
131 &self,
132 wallet_handle: &str,
133 private_key: [u8; 32],
134 ) -> Result<ImportKeyResponse, Error> {
135 let req = ImportKeyRequest {
136 wallet_handle_token: wallet_handle.to_string(),
137 private_key,
138 };
139 self.do_v1_request(req)
140 }
141
142 pub fn export_key(
146 &self,
147 wallet_handle: &str,
148 wallet_password: &str,
149 address: &str,
150 ) -> Result<ExportKeyResponse, Error> {
151 let req = ExportKeyRequest {
152 wallet_handle_token: wallet_handle.to_string(),
153 address: address.to_string(),
154 wallet_password: wallet_password.to_string(),
155 };
156 self.do_v1_request(req)
157 }
158
159 pub fn generate_key(&self, wallet_handle: &str) -> Result<GenerateKeyResponse, Error> {
161 let req = GenerateKeyRequest {
162 wallet_handle_token: wallet_handle.to_string(),
163 display_mnemonic: false,
164 };
165 self.do_v1_request(req)
166 }
167
168 pub fn delete_key(
170 &self,
171 wallet_handle: &str,
172 wallet_password: &str,
173 address: &str,
174 ) -> Result<DeleteKeyResponse, Error> {
175 let req = DeleteKeyRequest {
176 wallet_handle_token: wallet_handle.to_string(),
177 wallet_password: wallet_password.to_string(),
178 address: address.to_string(),
179 };
180 self.do_v1_request(req)
181 }
182
183 pub fn list_keys(&self, wallet_handle: &str) -> Result<ListKeysResponse, Error> {
185 let req = ListKeysRequest {
186 wallet_handle_token: wallet_handle.to_string(),
187 };
188 self.do_v1_request(req)
189 }
190
191 pub fn sign_transaction(
193 &self,
194 wallet_handle: &str,
195 wallet_password: &str,
196 transaction: &Transaction,
197 ) -> Result<SignTransactionResponse, Error> {
198 let transaction_bytes = rmp_serde::to_vec_named(transaction)?;
199 let req = SignTransactionRequest {
200 wallet_handle_token: wallet_handle.to_string(),
201 transaction: transaction_bytes,
202 wallet_password: wallet_password.to_string(),
203 };
204 self.do_v1_request(req)
205 }
206
207 pub fn list_multisig(&self, wallet_handle: &str) -> Result<ListMultisigResponse, Error> {
209 let req = ListMultisigRequest {
210 wallet_handle_token: wallet_handle.to_string(),
211 };
212 self.do_v1_request(req)
213 }
214
215 pub fn import_multisig(
217 &self,
218 wallet_handle: &str,
219 version: u8,
220 threshold: u8,
221 pks: &[Ed25519PublicKey],
222 ) -> Result<ImportMultisigResponse, Error> {
223 let req = ImportMultisigRequest {
224 wallet_handle_token: wallet_handle.to_string(),
225 multisig_version: version,
226 threshold,
227 pks: pks.to_vec(),
228 };
229 self.do_v1_request(req)
230 }
231
232 pub fn export_multisig(
234 &self,
235 wallet_handle: &str,
236 address: &str,
237 ) -> Result<ExportMultisigResponse, Error> {
238 let req = ExportMultisigRequest {
239 wallet_handle_token: wallet_handle.to_string(),
240 address: address.to_string(),
241 };
242 self.do_v1_request(req)
243 }
244
245 pub fn delete_multisig(
247 &self,
248 wallet_handle: &str,
249 wallet_password: &str,
250 address: &str,
251 ) -> Result<DeleteMultisigResponse, Error> {
252 let req = DeleteMultisigRequest {
253 wallet_handle_token: wallet_handle.to_string(),
254 wallet_password: wallet_password.to_string(),
255 address: address.to_string(),
256 };
257 self.do_v1_request(req)
258 }
259
260 pub fn sign_multisig_transaction(
262 &self,
263 wallet_handle: &str,
264 wallet_password: &str,
265 transaction: &Transaction,
266 public_key: Ed25519PublicKey,
267 partial_multisig: Option<MultisigSignature>,
268 ) -> Result<SignMultisigTransactionResponse, Error> {
269 let transaction_bytes = rmp_serde::to_vec_named(transaction)?;
270 let req = SignMultisigTransactionRequest {
271 wallet_handle_token: wallet_handle.to_string(),
272 transaction: transaction_bytes,
273 public_key,
274 partial_multisig,
275 wallet_password: wallet_password.to_string(),
276 };
277 self.do_v1_request(req)
278 }
279
280 fn do_v1_request<R>(&self, req: R) -> Result<R::Response, Error>
281 where
282 R: APIV1Request,
283 {
284 let response = self
285 .http_client
286 .request(R::METHOD, &format!("{}/{}", self.address, R::PATH))
287 .header(KMD_TOKEN_HEADER, &self.token)
288 .header("Accept", "application/json")
289 .json(&req)
290 .send()?
291 .text()?;
292 if let Ok(envelope) = serde_json::from_str::<APIV1ResponseEnvelope>(&response) {
293 if envelope.error {
294 return Err(Error::Api(envelope.message));
295 }
296 }
297 Ok(serde_json::from_str(&response)?)
298 }
299}
300
301pub mod requests {
302 use reqwest::Method;
303 use serde::de::DeserializeOwned;
304 use serde::Serialize;
305
306 use crate::crypto::MultisigSignature;
307 use crate::kmd::responses::*;
308 use crate::util::serialize_bytes;
309 use crate::{Ed25519PublicKey, MasterDerivationKey};
310
311 pub trait APIV1Request: Serialize {
312 type Response: DeserializeOwned;
313 const PATH: &'static str;
314 const METHOD: Method;
315 }
316
317 #[derive(Serialize)]
319 pub struct VersionsRequest;
320
321 #[derive(Serialize)]
323 pub struct ListWalletsRequest;
324
325 #[derive(Serialize)]
326 pub struct CreateWalletRequest {
327 pub master_derivation_key: MasterDerivationKey,
328 pub wallet_driver_name: String,
329 pub wallet_name: String,
330 pub wallet_password: String,
331 }
332
333 #[derive(Serialize)]
335 pub struct InitWalletHandleRequest {
336 pub wallet_id: String,
337 pub wallet_password: String,
338 }
339
340 #[derive(Serialize)]
342 pub struct ReleaseWalletHandleRequest {
343 pub wallet_handle_token: String,
344 }
345
346 #[derive(Serialize)]
348 pub struct RenewWalletHandleRequest {
349 pub wallet_handle_token: String,
350 }
351
352 #[derive(Serialize)]
354 pub struct RenameWalletRequest {
355 pub wallet_id: String,
356 pub wallet_password: String,
357 pub wallet_name: String,
358 }
359
360 #[derive(Serialize)]
362 pub struct GetWalletRequest {
363 pub wallet_handle_token: String,
364 }
365
366 #[derive(Serialize)]
368 pub struct ExportMasterDerivationKeyRequest {
369 pub wallet_handle_token: String,
370 pub wallet_password: String,
371 }
372
373 #[derive(Serialize)]
375 pub struct ImportKeyRequest {
376 pub wallet_handle_token: String,
377 #[serde(serialize_with = "serialize_bytes")]
378 pub private_key: [u8; 32],
379 }
380
381 #[derive(Serialize)]
383 pub struct ExportKeyRequest {
384 pub wallet_handle_token: String,
385 pub address: String,
386 pub wallet_password: String,
387 }
388
389 #[derive(Serialize)]
391 pub struct GenerateKeyRequest {
392 pub wallet_handle_token: String,
393 pub display_mnemonic: bool,
394 }
395
396 #[derive(Serialize)]
398 pub struct DeleteKeyRequest {
399 pub wallet_handle_token: String,
400 pub address: String,
401 pub wallet_password: String,
402 }
403
404 #[derive(Serialize)]
406 pub struct ListKeysRequest {
407 pub wallet_handle_token: String,
408 }
409
410 #[derive(Serialize)]
412 pub struct SignTransactionRequest {
413 pub wallet_handle_token: String,
414 #[serde(serialize_with = "serialize_bytes")]
415 pub transaction: Vec<u8>,
416 pub wallet_password: String,
417 }
418
419 #[derive(Serialize)]
421 pub struct ListMultisigRequest {
422 pub wallet_handle_token: String,
423 }
424
425 #[derive(Serialize)]
427 pub struct ImportMultisigRequest {
428 pub wallet_handle_token: String,
429 pub multisig_version: u8,
430 pub threshold: u8,
431 pub pks: Vec<Ed25519PublicKey>,
432 }
433
434 #[derive(Serialize)]
436 pub struct ExportMultisigRequest {
437 pub wallet_handle_token: String,
438 pub address: String,
439 }
440
441 #[derive(Serialize)]
443 pub struct DeleteMultisigRequest {
444 pub wallet_handle_token: String,
445 pub address: String,
446 pub wallet_password: String,
447 }
448
449 #[derive(Serialize)]
451 pub struct SignMultisigTransactionRequest {
452 pub wallet_handle_token: String,
453 #[serde(serialize_with = "serialize_bytes")]
454 pub transaction: Vec<u8>,
455 pub public_key: Ed25519PublicKey,
456 pub partial_multisig: Option<MultisigSignature>,
457 pub wallet_password: String,
458 }
459
460 impl APIV1Request for VersionsRequest {
461 type Response = VersionsResponse;
462 const PATH: &'static str = "versions";
463 const METHOD: Method = Method::GET;
464 }
465
466 impl APIV1Request for ListWalletsRequest {
467 type Response = ListWalletsResponse;
468 const PATH: &'static str = "v1/wallets";
469 const METHOD: Method = Method::GET;
470 }
471
472 impl APIV1Request for CreateWalletRequest {
473 type Response = CreateWalletResponse;
474 const PATH: &'static str = "v1/wallet";
475 const METHOD: Method = Method::POST;
476 }
477
478 impl APIV1Request for InitWalletHandleRequest {
479 type Response = InitWalletHandleResponse;
480 const PATH: &'static str = "v1/wallet/init";
481 const METHOD: Method = Method::POST;
482 }
483
484 impl APIV1Request for ReleaseWalletHandleRequest {
485 type Response = ReleaseWalletHandleResponse;
486 const PATH: &'static str = "v1/wallet/release";
487 const METHOD: Method = Method::POST;
488 }
489
490 impl APIV1Request for RenewWalletHandleRequest {
491 type Response = RenewWalletHandleResponse;
492 const PATH: &'static str = "v1/wallet/renew";
493 const METHOD: Method = Method::POST;
494 }
495
496 impl APIV1Request for RenameWalletRequest {
497 type Response = RenameWalletResponse;
498 const PATH: &'static str = "v1/wallet/rename";
499 const METHOD: Method = Method::POST;
500 }
501
502 impl APIV1Request for GetWalletRequest {
503 type Response = GetWalletResponse;
504 const PATH: &'static str = "v1/wallet/info";
505 const METHOD: Method = Method::POST;
506 }
507
508 impl APIV1Request for ExportMasterDerivationKeyRequest {
509 type Response = ExportMasterDerivationKeyResponse;
510 const PATH: &'static str = "v1/master-key/export";
511 const METHOD: Method = Method::POST;
512 }
513
514 impl APIV1Request for ImportKeyRequest {
515 type Response = ImportKeyResponse;
516 const PATH: &'static str = "v1/key/import";
517 const METHOD: Method = Method::POST;
518 }
519
520 impl APIV1Request for ExportKeyRequest {
521 type Response = ExportKeyResponse;
522 const PATH: &'static str = "v1/key/export";
523 const METHOD: Method = Method::POST;
524 }
525
526 impl APIV1Request for GenerateKeyRequest {
527 type Response = GenerateKeyResponse;
528 const PATH: &'static str = "v1/key";
529 const METHOD: Method = Method::POST;
530 }
531
532 impl APIV1Request for DeleteKeyRequest {
533 type Response = DeleteKeyResponse;
534 const PATH: &'static str = "v1/key";
535 const METHOD: Method = Method::DELETE;
536 }
537
538 impl APIV1Request for ListKeysRequest {
539 type Response = ListKeysResponse;
540 const PATH: &'static str = "v1/key/list";
541 const METHOD: Method = Method::POST;
542 }
543
544 impl APIV1Request for SignTransactionRequest {
545 type Response = SignTransactionResponse;
546 const PATH: &'static str = "v1/transaction/sign";
547 const METHOD: Method = Method::POST;
548 }
549
550 impl APIV1Request for ListMultisigRequest {
551 type Response = ListMultisigResponse;
552 const PATH: &'static str = "v1/multisig/list";
553 const METHOD: Method = Method::POST;
554 }
555
556 impl APIV1Request for ImportMultisigRequest {
557 type Response = ImportMultisigResponse;
558 const PATH: &'static str = "v1/multisig/import";
559 const METHOD: Method = Method::POST;
560 }
561
562 impl APIV1Request for ExportMultisigRequest {
563 type Response = ExportMultisigResponse;
564 const PATH: &'static str = "v1/multisig/export";
565 const METHOD: Method = Method::POST;
566 }
567
568 impl APIV1Request for DeleteMultisigRequest {
569 type Response = DeleteMultisigResponse;
570 const PATH: &'static str = "v1/multisig";
571 const METHOD: Method = Method::DELETE;
572 }
573
574 impl APIV1Request for SignMultisigTransactionRequest {
575 type Response = SignMultisigTransactionResponse;
576 const PATH: &'static str = "v1/multisig/sign";
577 const METHOD: Method = Method::POST;
578 }
579}
580
581pub mod responses {
582 use data_encoding::BASE64;
583 use serde::{Deserialize, Deserializer};
584
585 use crate::util::{deserialize_bytes, deserialize_bytes64, deserialize_mdk};
586
587 use crate::{Ed25519PublicKey, MasterDerivationKey};
588
589 use crate::kmd::{APIV1Wallet, APIV1WalletHandle};
590
591 #[derive(Debug, Deserialize)]
592 pub struct APIV1ResponseEnvelope {
593 pub error: bool,
594 pub message: String,
595 }
596
597 #[derive(Debug, Deserialize)]
599 pub struct VersionsResponse {
600 #[serde(default)]
601 pub versions: Vec<String>,
602 }
603
604 #[derive(Debug, Deserialize)]
606 pub struct ListWalletsResponse {
607 #[serde(default)]
608 pub wallets: Vec<APIV1Wallet>,
609 }
610
611 #[derive(Debug, Deserialize)]
612 pub struct CreateWalletResponse {
613 pub wallet: APIV1Wallet,
614 }
615
616 #[derive(Debug, Deserialize)]
618 pub struct InitWalletHandleResponse {
619 pub wallet_handle_token: String,
620 }
621
622 #[derive(Debug, Deserialize)]
624 pub struct ReleaseWalletHandleResponse {}
625
626 #[derive(Debug, Deserialize)]
628 pub struct RenewWalletHandleResponse {
629 pub wallet_handle: APIV1WalletHandle,
630 }
631
632 #[derive(Debug, Deserialize)]
634 pub struct RenameWalletResponse {
635 pub wallet: APIV1Wallet,
636 }
637
638 #[derive(Debug, Deserialize)]
640 pub struct GetWalletResponse {
641 pub wallet_handle: APIV1WalletHandle,
642 }
643
644 #[derive(Debug, Deserialize)]
646 pub struct ExportMasterDerivationKeyResponse {
647 #[serde(deserialize_with = "deserialize_mdk")]
648 pub master_derivation_key: MasterDerivationKey,
649 }
650
651 #[derive(Debug, Deserialize)]
653 pub struct ImportKeyResponse {
654 pub address: String,
655 }
656
657 #[derive(Deserialize)]
659 pub struct ExportKeyResponse {
660 #[serde(deserialize_with = "deserialize_bytes64")]
661 pub private_key: [u8; 64],
662 }
663
664 #[derive(Debug, Deserialize)]
666 pub struct GenerateKeyResponse {
667 pub address: String,
668 }
669
670 #[derive(Debug, Deserialize)]
672 pub struct DeleteKeyResponse {}
673
674 #[derive(Debug, Deserialize)]
676 pub struct ListKeysResponse {
677 pub addresses: Vec<String>,
678 }
679
680 #[derive(Debug, Deserialize)]
682 pub struct SignTransactionResponse {
683 #[serde(deserialize_with = "deserialize_bytes")]
684 pub signed_transaction: Vec<u8>,
685 }
686
687 #[derive(Debug, Deserialize)]
689 pub struct ListMultisigResponse {
690 #[serde(default)]
691 pub addresses: Vec<String>,
692 }
693
694 #[derive(Debug, Deserialize)]
696 pub struct ImportMultisigResponse {
697 pub address: String,
698 }
699
700 #[derive(Debug, Deserialize)]
702 pub struct ExportMultisigResponse {
703 pub multisig_version: u8,
704 pub threshold: u8,
705 #[serde(deserialize_with = "deserialize_public_keys")]
706 pub pks: Vec<Ed25519PublicKey>,
707 }
708
709 #[derive(Debug, Deserialize)]
711 pub struct DeleteMultisigResponse {}
712
713 #[derive(Debug, Deserialize)]
715 pub struct SignMultisigTransactionResponse {
716 #[serde(deserialize_with = "deserialize_bytes")]
717 pub multisig: Vec<u8>,
718 }
719
720 fn deserialize_public_keys<'de, D>(deserializer: D) -> Result<Vec<Ed25519PublicKey>, D::Error>
721 where
722 D: Deserializer<'de>,
723 {
724 use serde::de::Error;
725 <Vec<String>>::deserialize(deserializer)?
726 .iter()
727 .map(|string| {
728 let mut decoded = [0; 32];
729 let bytes = BASE64.decode(string.as_bytes()).map_err(D::Error::custom)?;
730 decoded.copy_from_slice(&bytes);
731 Ok(Ed25519PublicKey(decoded))
732 })
733 .collect()
734 }
735}
736
737#[derive(Debug, Deserialize)]
738pub struct APIV1Wallet {
739 pub id: String,
740 pub name: String,
741 pub driver_name: String,
742 pub driver_version: u32,
743 pub mnemonic_ux: bool,
744 pub supported_txs: Vec<String>,
745}
746
747#[derive(Debug, Deserialize)]
748pub struct APIV1WalletHandle {
749 pub wallet: APIV1Wallet,
750 pub expires_seconds: i64,
751}