1#![allow(clippy::needless_for_each)]
2use crate::{AppState, VERSION};
3use axum::Json;
4use axum::{Extension, response::IntoResponse};
5use axum_extra::extract::Query;
6use hyper::StatusCode;
7use serde::{Deserialize, Serialize};
8use serde_json::{Value, json};
9use std::collections::HashSet;
10use utoipa::{IntoParams, OpenApi, ToSchema};
11
12#[derive(OpenApi)]
13#[openapi(paths(
14 hello,
15 create_key,
16 sign_transaction_hash,
17 sign_transaction,
18 verify_signature,
19 delete_key,
20 list_keys,
21))]
22pub struct ApiDoc;
23
24#[derive(Deserialize, IntoParams)]
25pub struct HelloParams {
26 message: Option<String>,
27}
28
29#[derive(Serialize, ToSchema)]
30struct HelloResponse {
31 message: String,
32}
33
34#[utoipa::path(
35 get,
36 path = "/",
37 responses(
38 (status = 200, description = "Hello message", body = HelloResponse)
39 )
40)]
41pub async fn hello(
42 Query(params): Query<HelloParams>,
43 Extension(state): Extension<AppState>,
44) -> impl IntoResponse {
45 let message = if state.config.is_testing_mode() {
46 params.message.map_or_else(
47 || "KMS TESTING_MODE".to_string(),
48 |message| format!("KMS TESTING_MODE {message}"),
49 )
50 } else {
51 params.message.unwrap_or_else(|| "KMS".to_string())
52 };
53
54 format!("Hello {message}! Version: {}", *VERSION)
55}
56
57#[derive(Serialize, ToSchema, Deserialize, Debug, Clone)]
58pub struct CreateKeyResponse {
59 pub address: String,
60 pub public_key: String,
61}
62
63#[utoipa::path(
64 post,
65 path = "/createKey",
66 responses(
67 (status = 201, description = "Successful key generation", body = CreateKeyResponse),
68 ),
69 tag = "Key Management"
70)]
71pub async fn create_key(Extension(state): Extension<AppState>) -> impl IntoResponse {
72 let mut keys_service = state.keys_service.lock().await;
73
74 match keys_service.create_key(&state.config).await {
75 Ok(key) => (
76 StatusCode::CREATED,
77 Json(json!({
78 "public_key": key.public_key,
79 "address": key.address
80 })),
81 ),
82 Err(err) => (
83 StatusCode::INTERNAL_SERVER_ERROR,
84 Json(json!({ "error": err })),
85 ),
86 }
87}
88
89#[derive(Serialize, ToSchema, Deserialize, Debug)]
90pub struct Approval {
91 pub address: String,
92 pub signer: String,
93 pub r: String,
94 pub s: String,
95 pub v: String,
96 pub hash: String,
97 pub signature: String,
98}
99
100#[utoipa::path(
101 post,
102 path = "/signTransactionHash",
103 params(
104 ("keys" = Vec<String>, Query, description = "List of addresses or public keys to sign the transaction with"),
105 ),
106 request_body(
107 content = String,
108 description = "The transaction hash as hexadecimal",
109 example = "0x..."
110 ),
111 responses(
112 (status = 200, description = "Signatures for each key", body = Vec<Approval>),
113 (status = 500, description = "Internal server error", body = String)
114 ),
115 tag = "Signature Management"
116)]
117pub async fn sign_transaction_hash(
118 Extension(state): Extension<AppState>,
119 Query(query): Query<SignTransactionParams>,
120 transaction_hash: String,
121) -> impl IntoResponse {
122 let mut keys_service = state.keys_service.lock().await;
123
124 let unique_keys: HashSet<_> = query.keys.iter().cloned().collect();
125
126 if unique_keys.len() > 10 {
127 return (
128 StatusCode::BAD_REQUEST,
129 Json(json!({
130 "error": "Too many public keys"
131 })),
132 );
133 }
134
135 let mut approvals = Vec::new();
136
137 for key in unique_keys {
138 match keys_service
139 .sign_transaction_hash(&state.config, &transaction_hash, &key)
140 .await
141 {
142 Ok(key_entry) => {
143 let sig_clean = key_entry
144 .signature
145 .strip_prefix("0x")
146 .unwrap_or(&key_entry.signature);
147 let mut sig_bytes = match hex::decode(sig_clean) {
148 Ok(bytes) if bytes.len() == 64 || bytes.len() == 65 => bytes,
149 _ => {
150 return (
151 StatusCode::INTERNAL_SERVER_ERROR,
152 Json(json!({ "error": "Invalid signature format" })),
153 );
154 }
155 };
156
157 if state.config.is_casper_mode() {
159 sig_bytes = sig_bytes[1..].to_vec();
160 }
161
162 let r = &sig_bytes[0..32];
163 let s = &sig_bytes[32..64];
164 let v = if sig_bytes.len() == 65 {
165 sig_bytes[64]
166 } else {
167 0 };
169
170 approvals.push(Approval {
171 address: key_entry.address.to_string(),
172 signer: key_entry.public_key.to_string(),
173 v: format!("{v:02x}"),
174 r: hex::encode(r),
175 s: hex::encode(s),
176 hash: transaction_hash.clone(),
177 signature: key_entry.signature.to_string().clone(),
178 });
179 }
180 Err(err) => {
181 return (
182 StatusCode::INTERNAL_SERVER_ERROR,
183 Json(json!({ "error": format!("Error signing for {}: {}", key, err) })),
184 );
185 }
186 }
187 }
188
189 let approvals_json = serde_json::to_value(&approvals).unwrap_or_default();
190
191 (StatusCode::OK, Json(approvals_json))
192}
193
194#[derive(Deserialize, IntoParams)]
195pub struct SignTransactionParams {
196 #[param(min_items = 1, max_items = 10)]
197 pub keys: Vec<String>,
198}
199
200#[utoipa::path(
201 post,
202 path = "/signTransaction",
203 params(
204 ("keys" = Vec<String>, Query, description = "List of addresses or public keys to sign the transaction with"),
205 ),
206 request_body(
207 content = Value,
208 description = "The transaction (json format)",
209 example = json!({}),
210 ),
211 responses(
212 (status = 200, description = "Signed transaction", body = Value),
213 (status = 500, description = "Internal server error", body = String)
214 ),
215 tag = "Signature Management"
216)]
217pub async fn sign_transaction(
218 Extension(state): Extension<AppState>,
219 Query(query): Query<SignTransactionParams>,
220 Json(body): Json<Value>,
221) -> impl IntoResponse {
222 let mut keys_service = state.keys_service.lock().await;
223
224 let unique_keys: HashSet<_> = query.keys.iter().cloned().collect();
225
226 if unique_keys.len() > 10 {
227 return (
228 StatusCode::BAD_REQUEST,
229 Json(json!({
230 "error": "Too many public keys"
231 })),
232 );
233 }
234
235 let mut current_signed = body.to_string();
237
238 for key in unique_keys {
239 match keys_service
240 .sign_transaction(&state.config, ¤t_signed, &key)
241 .await
242 {
243 Ok(signed) => {
244 current_signed = signed;
246 }
247 Err(err) => {
248 return (
249 StatusCode::INTERNAL_SERVER_ERROR,
250 Json(json!({
251 "error": format!("Failed to sign with key {}: {}", key, err)
252 })),
253 );
254 }
255 }
256 }
257
258 let final_value: Value = match serde_json::from_str(¤t_signed) {
260 Ok(v) => v,
261 Err(_) => {
262 return (
263 StatusCode::INTERNAL_SERVER_ERROR,
264 Json(json!({
265 "error": "Failed to parse final signed transaction"
266 })),
267 );
268 }
269 };
270
271 (StatusCode::OK, Json(final_value))
272}
273
274#[derive(Deserialize, IntoParams)]
275pub struct VerifySignatureParams {
276 pub transaction_hash: String,
277 pub key: String,
278 pub signature: String,
279 pub via_kms: Option<bool>,
280}
281
282#[utoipa::path(
283 get,
284 path = "/verifySignature",
285 params(
286 ("key" = String, Query, description = "The address or public key of the key to verify against"),
287 ("transaction_hash" = String, Query, description = "The original transaction hash"),
288 ("signature" = String, Query, description = "The signature to verify (with prefix)"),
289 ("via_kms" = Option<bool>, Query, description = "Whether to verify the signature using AWS KMS (default: false)")
290 ),
291 responses(
292 (status = 200, description = "Verification result", body = bool),
293 (status = 500, description = "Internal server error", body = String)
294 ),
295 tag = "Signature Management"
296)]
297pub async fn verify_signature(
298 Extension(state): Extension<AppState>,
299 Query(params): Query<VerifySignatureParams>,
300) -> impl IntoResponse {
301 let mut keys_service = state.keys_service.lock().await;
302
303 let via_kms = params.via_kms.unwrap_or(false);
304
305 let result = if via_kms {
306 keys_service
307 .verify_via_kms(¶ms.transaction_hash, ¶ms.signature, ¶ms.key)
308 .await
309 } else {
310 keys_service
311 .verify(¶ms.transaction_hash, ¶ms.signature, ¶ms.key)
312 .await
313 };
314
315 match result {
316 Ok(valid) => (StatusCode::OK, Json(json!({ "valid": valid }))),
317 Err(err) => (
318 StatusCode::INTERNAL_SERVER_ERROR,
319 Json(json!({ "error": err })),
320 ),
321 }
322}
323
324#[derive(Deserialize, IntoParams)]
325pub struct DeleteKeyParams {
326 pub key: String,
327}
328
329#[utoipa::path(
330 delete,
331 path = "/deleteKey",
332 params(
333 ("key" = String, Query, description = "Address or public key of the key to delete")
334 ),
335 responses(
336 (status = 200, description = "Key deletion status", body = bool),
337 (status = 404, description = "Delete feature is disabled"),
338 (status = 500, description = "Internal server error", body = String)
339 ),
340 tag = "Key Management"
341)]
342pub async fn delete_key(
343 Extension(state): Extension<AppState>,
344 Query(params): Query<DeleteKeyParams>,
345) -> impl IntoResponse {
346 let mut keys_service = state.keys_service.lock().await;
347
348 let key = params.key.trim();
349
350 if key.is_empty() {
351 return (
352 StatusCode::BAD_REQUEST,
353 Json(json!({ "error": "Key cannot be empty" })),
354 );
355 }
356
357 match keys_service.delete_key(key).await {
358 Ok(result) => (StatusCode::OK, Json(json!({ "deleted": result }))),
359 Err(err) => (
360 StatusCode::INTERNAL_SERVER_ERROR,
361 Json(json!({ "error": err })),
362 ),
363 }
364}
365
366#[derive(Serialize, Deserialize, ToSchema, Eq, PartialEq, Debug, Clone)]
367struct KeyEntryResponse {
368 pub address: String,
369 pub public_key_base64: String,
370 pub public_key: String,
371 pub key_id: String,
372}
373
374#[utoipa::path(
375 get,
376 path = "/listKeys",
377 responses(
378 (status = 200, description = "List of keys", body = Vec<KeyEntryResponse>),
379 (status = 404, description = "Listing of keys feature is disabled"),
380 (status = 500, description = "Internal server error", body = String)
381 ),
382 tag = "Key Management"
383)]
384pub async fn list_keys(Extension(state): Extension<AppState>) -> impl IntoResponse {
385 let mut keys_service = state.keys_service.lock().await;
386
387 match keys_service.list_keys().await {
388 Ok(keys) => {
389 let keys_entries: Vec<KeyEntryResponse> = keys
390 .into_iter()
391 .map(|key_entry| KeyEntryResponse {
392 address: key_entry.address.to_string(),
393 public_key_base64: key_entry.public_key_base64.to_string(),
394 public_key: key_entry
395 .public_key
396 .as_deref()
397 .unwrap_or_default()
398 .to_string(),
399 key_id: key_entry.key_id.to_string(),
400 })
401 .collect();
402
403 if keys_entries.is_empty() {
404 return (
405 StatusCode::INTERNAL_SERVER_ERROR,
406 Json(json!({ "error": "No keys found" })),
407 );
408 }
409
410 (StatusCode::OK, Json(json!({ "keys": keys_entries })))
411 }
412 Err(err) => (
413 StatusCode::INTERNAL_SERVER_ERROR,
414 Json(json!({ "error": err })),
415 ),
416 }
417}
418
419#[cfg(test)]
420mod tests_routes {
421 use super::*;
422 use crate::{
423 AppState,
424 config::{Config, ConfigBuilder},
425 constants::{CASPER_PUBLIC_KEY_PREFIXED, SIGNATURE_PREFIXED, TRANSACTION_HASH},
426 services::keys_service::{KeyEntry, KeysServiceTrait, SigEntry},
427 };
428 use async_trait::async_trait;
429 use base64::Engine;
430 use base64::engine::general_purpose::STANDARD;
431 use http_body_util::BodyExt;
432 use std::sync::Arc;
433 use tokio::sync::Mutex;
434
435 #[derive(Default, Debug)]
436 struct MockKeysService {
437 keys: Vec<KeyEntry>,
438 }
439
440 #[async_trait]
441 impl KeysServiceTrait for MockKeysService {
442 async fn create_key(
443 &mut self,
444 _config: &crate::config::Config,
445 ) -> Result<KeyEntry, String> {
446 let public_key = CASPER_PUBLIC_KEY_PREFIXED.to_string();
447 Ok(KeyEntry {
448 public_key: Some(public_key).into(),
449 address: "address".to_string().into(),
450 public_key_base64: STANDARD.encode("public_key_base64").into(),
451 key_id: "key_id".to_string().into(),
452 })
453 }
454
455 async fn list_keys(&mut self) -> Result<Vec<KeyEntry>, String> {
456 Ok(self.keys.clone())
457 }
458
459 async fn sign_transaction(
460 &mut self,
461 _config: &crate::config::Config,
462 tx: &str,
463 public_key: &str,
464 ) -> Result<String, String> {
465 if public_key == "fail" {
466 return Err("Forced signature failure".to_string());
467 }
468 let mut value: serde_json::Value = serde_json::from_str(tx)
469 .map_err(|e| format!("Invalid JSON in transaction: {e}"))?;
470
471 match &mut value {
472 serde_json::Value::Object(map) => match map.get_mut("signed_by") {
473 Some(serde_json::Value::Array(arr)) => {
474 arr.push(serde_json::Value::String(public_key.to_string()));
475 }
476 Some(_) => {
477 let old = map.remove("signed_by").unwrap();
478 map.insert(
479 "signed_by".to_string(),
480 serde_json::Value::Array(vec![
481 old,
482 serde_json::Value::String(public_key.to_string()),
483 ]),
484 );
485 }
486 None => {
487 map.insert(
488 "signed_by".to_string(),
489 serde_json::Value::Array(vec![serde_json::Value::String(
490 public_key.to_string(),
491 )]),
492 );
493 }
494 },
495 _ => {
496 return Err("Transaction JSON must be an object".to_string());
497 }
498 }
499 serde_json::to_string(&value).map_err(|e| e.to_string())
500 }
501
502 async fn sign_transaction_hash(
503 &mut self,
504 _config: &crate::config::Config,
505 _transaction_hash: &str,
506 public_key: &str,
507 ) -> Result<SigEntry, String> {
508 Ok(SigEntry {
509 address: public_key.to_string().into(),
510 public_key: public_key.to_string().into(),
511 signature: SIGNATURE_PREFIXED.to_string().into(),
512 })
513 }
514
515 async fn verify(
516 &mut self,
517 _transaction_hash_hex: &str,
518 signature_hex: &str,
519 public_key: &str,
520 ) -> Result<bool, String> {
521 Ok(signature_hex == "valid" && public_key == "pubkey")
522 }
523
524 async fn verify_via_kms(
525 &mut self,
526 _transaction_hash_hex: &str,
527 signature_hex: &str,
528 public_key: &str,
529 ) -> Result<bool, String> {
530 Ok(signature_hex == "kms-valid" && public_key == "pubkey")
531 }
532
533 async fn delete_key(&mut self, key: &str) -> Result<bool, String> {
534 let before = self.keys.len();
535 self.keys.retain(|key_entry| {
536 key != key_entry.address.as_str() && key_entry.public_key.as_deref() != Some(key)
537 });
538 let after = self.keys.len();
539 Ok(after < before)
540 }
541 }
542
543 #[tokio::test]
544 async fn test_hello_with_custom_message() {
545 let params = HelloParams {
546 message: Some("World".to_string()),
547 };
548 let mock_service = MockKeysService::default();
549 let config = ConfigBuilder::new().with_testing_mode(false).build();
550 let state = AppState {
551 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
552 config,
553 };
554
555 let response = hello(Query(params), Extension(state)).await.into_response();
556
557 let status = response.status();
558 let body = response.into_body().collect().await.unwrap().to_bytes();
559 let body_str = String::from_utf8(body.to_vec()).unwrap();
560
561 assert_eq!(status, StatusCode::OK);
562 assert!(body_str.contains("Hello World!"));
563 assert!(body_str.contains(&format!("Version: {}", *VERSION)));
564 }
565
566 #[tokio::test]
567 async fn test_hello_with_mock_mode() {
568 let params = HelloParams { message: None };
569
570 let mock_service = MockKeysService::default();
571 let config = ConfigBuilder::new().with_testing_mode(true).build();
572 let state = AppState {
573 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
574 config,
575 };
576
577 let response = hello(Query(params), Extension(state)).await.into_response();
578
579 let status = response.status();
580 let body = response.into_body().collect().await.unwrap().to_bytes();
581 let body_str = String::from_utf8(body.to_vec()).unwrap();
582
583 assert_eq!(status, StatusCode::OK);
584 assert!(body_str.contains("Hello KMS TESTING_MODE!"));
585 }
586
587 #[tokio::test]
588 async fn test_create_key_success() {
589 let mock_service = MockKeysService::default();
590 let state = AppState {
591 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
592 config: Config::default(),
593 };
594
595 let response = create_key(Extension(state)).await.into_response();
596
597 assert_eq!(response.status(), StatusCode::CREATED);
598
599 let body = response.into_body().collect().await.unwrap().to_bytes();
600 let body_str = String::from_utf8(body.to_vec()).unwrap();
601
602 assert!(
603 body_str.contains(CASPER_PUBLIC_KEY_PREFIXED),
604 "Expected mocked public key in response"
605 );
606 }
607
608 #[tokio::test]
609 async fn test_list_keys_success() {
610 let mock_service = MockKeysService {
611 keys: vec![
612 KeyEntry {
613 address: "address_1".to_string().into(),
614 public_key_base64: STANDARD.encode("public_key_1_base64").into(),
615 public_key: Some("public_key_1".to_string()).into(),
616 key_id: "key_id_1".to_string().into(),
617 },
618 KeyEntry {
619 address: "address_2".to_string().into(),
620 public_key_base64: STANDARD.encode("public_key_2_base64").into(),
621 public_key: Some("public_key_2".to_string()).into(),
622 key_id: "key_id_2".to_string().into(),
623 },
624 ],
625 };
626
627 let state = AppState {
628 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
629 config: Config::default(),
630 };
631
632 let response = list_keys(Extension(state)).await.into_response();
633
634 assert_eq!(response.status(), StatusCode::OK);
635 let body = response.into_body().collect().await.unwrap().to_bytes();
636 let body_str = String::from_utf8(body.to_vec()).unwrap();
637
638 let json_val: serde_json::Value = serde_json::from_str(&body_str).unwrap();
639 let keys = json_val.get("keys").unwrap().as_array().unwrap();
640
641 assert_eq!(keys.len(), 2);
642 assert_eq!(keys[0]["address"], "address_1");
643 assert_eq!(
644 keys[0]["public_key_base64"],
645 STANDARD.encode("public_key_1_base64")
646 );
647 assert_eq!(keys[0]["public_key"], "public_key_1");
648 assert_eq!(keys[0]["key_id"], "key_id_1");
649 assert_eq!(keys[1]["address"], "address_2");
650 assert_eq!(
651 keys[1]["public_key_base64"],
652 STANDARD.encode("public_key_2_base64")
653 );
654 assert_eq!(keys[1]["public_key"], "public_key_2");
655 assert_eq!(keys[1]["key_id"], "key_id_2");
656 }
657
658 #[tokio::test]
659 async fn test_delete_key_success_from_address() {
660 let mock_service = MockKeysService {
661 keys: vec![
662 KeyEntry {
663 address: "address_1".to_string().into(),
664 public_key_base64: STANDARD.encode("public_key_1_base64").into(),
665 public_key: Some("public_key_1".to_string()).into(),
666 key_id: "key_id_1".to_string().into(),
667 },
668 KeyEntry {
669 address: "address_2".to_string().into(),
670 public_key_base64: STANDARD.encode("public_key_2_base64").into(),
671 public_key: Some("public_key_2".to_string()).into(),
672 key_id: "key_id_2".to_string().into(),
673 },
674 ],
675 };
676
677 let state = AppState {
678 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
679 config: Config::default(),
680 };
681
682 let params = DeleteKeyParams {
683 key: "address_1".to_string(),
684 };
685
686 let response = delete_key(Extension(state), Query(params))
687 .await
688 .into_response();
689
690 assert_eq!(response.status(), StatusCode::OK);
691
692 let body = response.into_body().collect().await.unwrap().to_bytes();
693 let body_json: serde_json::Value = serde_json::from_slice(&body).unwrap();
694
695 assert_eq!(body_json["deleted"], true);
696 }
697
698 #[tokio::test]
699 async fn test_delete_key_success() {
700 let mock_service = MockKeysService {
701 keys: vec![
702 KeyEntry {
703 address: "address_1".to_string().into(),
704 public_key_base64: STANDARD.encode("public_key_1_base64").into(),
705 public_key: Some("public_key_1".to_string()).into(),
706 key_id: "key_id_1".to_string().into(),
707 },
708 KeyEntry {
709 address: "address_2".to_string().into(),
710 public_key_base64: STANDARD.encode("public_key_2_base64").into(),
711 public_key: Some("public_key_2".to_string()).into(),
712 key_id: "key_id_2".to_string().into(),
713 },
714 ],
715 };
716
717 let state = AppState {
718 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
719 config: Config::default(),
720 };
721
722 let params = DeleteKeyParams {
723 key: "public_key_1".to_string(),
724 };
725
726 let response = delete_key(Extension(state), Query(params))
727 .await
728 .into_response();
729
730 assert_eq!(response.status(), StatusCode::OK);
731
732 let body = response.into_body().collect().await.unwrap().to_bytes();
733 let body_json: serde_json::Value = serde_json::from_slice(&body).unwrap();
734
735 assert_eq!(body_json["deleted"], true);
736 }
737
738 #[tokio::test]
739 async fn test_delete_key_with_none_or_empty_should_fail() {
740 let mock_service = MockKeysService { keys: vec![] };
741
742 let state = AppState {
743 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
744 config: Config::default(),
745 };
746
747 let params = DeleteKeyParams {
748 key: "".to_string(),
749 };
750
751 let response = delete_key(Extension(state.clone()), Query(params))
752 .await
753 .into_response();
754
755 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
756
757 let body = response.into_body().collect().await.unwrap().to_bytes();
758 let body_json: serde_json::Value = serde_json::from_slice(&body).unwrap();
759
760 assert!(
761 body_json["error"]
762 .as_str()
763 .unwrap()
764 .contains("Key cannot be empty")
765 );
766 }
767
768 #[tokio::test]
769 async fn test_delete_key_not_found() {
770 let mock_service = MockKeysService {
771 keys: vec![KeyEntry {
772 address: "address_1".to_string().into(),
773 public_key_base64: STANDARD.encode("public_key_1_base64").into(),
774 public_key: Some("public_key_1".to_string()).into(),
775 key_id: "key_id_1".to_string().into(),
776 }],
777 };
778
779 let state = AppState {
780 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
781 config: Config::default(),
782 };
783
784 let params = DeleteKeyParams {
785 key: "nonexistent".to_string(),
786 };
787
788 let response = delete_key(Extension(state), Query(params))
789 .await
790 .into_response();
791
792 assert_eq!(response.status(), StatusCode::OK);
793
794 let body = response.into_body().collect().await.unwrap().to_bytes();
795 let body_json: serde_json::Value = serde_json::from_slice(&body).unwrap();
796
797 assert_eq!(body_json["deleted"], false);
798 }
799
800 #[tokio::test]
801 async fn test_verify_signature_basic_success() {
802 let mock_service = MockKeysService::default();
803
804 let state = AppState {
805 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
806 config: Config::default(),
807 };
808
809 let params = VerifySignatureParams {
810 transaction_hash: "abc123".into(),
811 signature: "valid".into(),
812 key: "pubkey".into(),
813 via_kms: Some(false),
814 };
815
816 let response = verify_signature(Extension(state), Query(params))
817 .await
818 .into_response();
819
820 assert_eq!(response.status(), StatusCode::OK);
821 let body = response.into_body().collect().await.unwrap().to_bytes();
822 let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
823 assert_eq!(json["valid"], true);
824 }
825
826 #[tokio::test]
827 async fn test_verify_signature_via_kms_success() {
828 let mock_service = MockKeysService::default();
829
830 let state = AppState {
831 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
832 config: Config::default(),
833 };
834
835 let params = VerifySignatureParams {
836 transaction_hash: "abc123".into(),
837 signature: "kms-valid".into(),
838 key: "pubkey".into(),
839 via_kms: Some(true),
840 };
841
842 let response = verify_signature(Extension(state), Query(params))
843 .await
844 .into_response();
845
846 assert_eq!(response.status(), StatusCode::OK);
847 let body = response.into_body().collect().await.unwrap().to_bytes();
848 let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
849 assert_eq!(json["valid"], true);
850 }
851
852 #[tokio::test]
853 async fn test_verify_signature_failure() {
854 let mock_service = MockKeysService::default();
855
856 let state = AppState {
857 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
858 config: Config::default(),
859 };
860
861 let params = VerifySignatureParams {
862 transaction_hash: "abc123".into(),
863 signature: "invalid".into(),
864 key: "pubkey".into(),
865 via_kms: Some(false),
866 };
867
868 let response = verify_signature(Extension(state), Query(params))
869 .await
870 .into_response();
871
872 assert_eq!(response.status(), StatusCode::OK);
873 let body = response.into_body().collect().await.unwrap().to_bytes();
874 let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
875 assert_eq!(json["valid"], false);
876 }
877
878 #[tokio::test]
879 async fn test_sign_transaction_hash_success() {
880 let mock_service = MockKeysService::default();
881
882 let state = AppState {
883 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
884 config: Config::default(),
885 };
886
887 let query = SignTransactionParams {
888 keys: vec!["key1".to_string(), "key2".to_string(), "key1".to_string()],
889 };
890
891 let transaction_hash =
892 "d2ab5d10a332cdf3222b7ffecb5abd07b44f338be7193775465e10b3e4fe0299".to_string();
893
894 let response =
895 sign_transaction_hash(Extension(state), Query(query), transaction_hash.clone())
896 .await
897 .into_response();
898
899 assert_eq!(response.status(), StatusCode::OK);
900
901 let body = response.into_body().collect().await.unwrap().to_bytes();
902
903 let approvals: Vec<Approval> = serde_json::from_slice(&body).unwrap();
904 assert_eq!(approvals.len(), 2);
905
906 for approval in approvals {
907 assert!(approval.signer == "key1" || approval.signer == "key2");
908 assert_eq!(approval.signature, SIGNATURE_PREFIXED);
909 }
910 }
911
912 #[tokio::test]
913 async fn test_sign_transaction_hash_too_many_keys() {
914 let mock_service = MockKeysService::default();
915
916 let state = AppState {
917 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
918 config: Config::default(),
919 };
920
921 let mut keys = Vec::new();
922 for i in 0..11 {
923 keys.push(format!("key{i}"));
924 }
925 let query = SignTransactionParams { keys };
926
927 let transaction_hash = TRANSACTION_HASH.to_string();
928
929 let response = sign_transaction_hash(Extension(state), Query(query), transaction_hash)
930 .await
931 .into_response();
932
933 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
934
935 let body = response.into_body().collect().await.unwrap().to_bytes();
936 let json_val: serde_json::Value = serde_json::from_slice(&body).unwrap();
937
938 assert_eq!(json_val["error"], "Too many public keys");
939 }
940
941 #[tokio::test]
942 async fn test_sign_transaction_success() {
943 let mock_service = MockKeysService::default();
944
945 let state = AppState {
946 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
947 config: Config::default(),
948 };
949
950 let query = SignTransactionParams {
951 keys: vec!["keyA".to_string(), "keyB".to_string()],
952 };
953
954 let transaction_json = json!({
955 "foo": "bar"
956 });
957
958 let response = sign_transaction(Extension(state), Query(query), Json(transaction_json))
959 .await
960 .into_response();
961
962 assert_eq!(response.status(), StatusCode::OK);
963
964 let body = response.into_body().collect().await.unwrap().to_bytes();
965
966 let signed_tx: serde_json::Value = serde_json::from_slice(&body).unwrap();
967
968 let signed_by = signed_tx.get("signed_by").unwrap().as_array().unwrap();
969
970 assert!(signed_by.iter().any(|v| v == "keyA"));
971 assert!(signed_by.iter().any(|v| v == "keyB"));
972 }
973
974 #[tokio::test]
975 async fn test_sign_transaction_too_many_keys() {
976 let mock_service = MockKeysService::default();
977
978 let state = AppState {
979 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
980 config: Config::default(),
981 };
982
983 let query = SignTransactionParams {
984 keys: (0..11).map(|i| format!("key{i}")).collect(),
985 };
986
987 let transaction_json = json!({"foo": "bar"});
988
989 let response = sign_transaction(Extension(state), Query(query), Json(transaction_json))
990 .await
991 .into_response();
992
993 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
994
995 let body = response.into_body().collect().await.unwrap().to_bytes();
996 let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
997
998 assert_eq!(json["error"], "Too many public keys");
999 }
1000
1001 #[tokio::test]
1002 async fn test_sign_transaction_invalid_json() {
1003 let mock_service = MockKeysService::default();
1004
1005 let state = AppState {
1006 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
1007 config: Config::default(),
1008 };
1009
1010 let query = SignTransactionParams {
1011 keys: vec!["keyA".to_string()],
1012 };
1013
1014 let transaction_json = json!(["not", "an", "object"]);
1015
1016 let response = sign_transaction(Extension(state), Query(query), Json(transaction_json))
1017 .await
1018 .into_response();
1019
1020 assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
1021
1022 let body = response.into_body().collect().await.unwrap().to_bytes();
1023 let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
1024
1025 assert_eq!(
1026 json["error"],
1027 "Failed to sign with key keyA: Transaction JSON must be an object"
1028 );
1029 }
1030
1031 #[tokio::test]
1032 async fn test_sign_transaction_signature_failure() {
1033 let mock_service = MockKeysService::default();
1034
1035 let state = AppState {
1036 keys_service: Arc::new(Mutex::new(Box::new(mock_service))),
1037 config: Config::default(),
1038 };
1039
1040 let query = SignTransactionParams {
1041 keys: vec!["keyA".to_string(), "fail".to_string()],
1042 };
1043
1044 let transaction_json = json!({"foo": "bar"});
1045
1046 let response = sign_transaction(Extension(state), Query(query), Json(transaction_json))
1047 .await
1048 .into_response();
1049
1050 assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
1051
1052 let body = response.into_body().collect().await.unwrap().to_bytes();
1053 let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
1054
1055 assert!(
1056 json["error"]
1057 .as_str()
1058 .unwrap()
1059 .contains("Failed to sign with key fail")
1060 );
1061 }
1062}