kms_secp256k1_api/
routes.rs

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                // Strip 1 byte Casper prefix for r and s, should not have v
158                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 // default v for Casper or signatures without recovery id
168                };
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    // Start with the original unsigned transaction
236    let mut current_signed = body.to_string();
237
238    for key in unique_keys {
239        match keys_service
240            .sign_transaction(&state.config, &current_signed, &key)
241            .await
242        {
243            Ok(signed) => {
244                // Use this newly signed transaction as base for the next
245                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    // Deserialize once at the end to return clean JSON (not a quoted string)
259    let final_value: Value = match serde_json::from_str(&current_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(&params.transaction_hash, &params.signature, &params.key)
308            .await
309    } else {
310        keys_service
311            .verify(&params.transaction_hash, &params.signature, &params.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}