use std::sync::Arc;
use guardian_client::testing::mocks::{MockGuardianService, start_mock_server};
use guardian_shared::SignatureScheme;
use miden_protocol::Word;
use super::test_support::{
chain_with_notes, multisig_account_with_scheme, offline_client_parts_with_keystore,
registered_state,
};
use crate::account::MultisigAccount;
use crate::client::MultisigClient;
use crate::execution::build_final_transaction_request;
use crate::keystore::{EcdsaGuardianKeyStore, GuardianKeyStore, KeyManager};
use crate::proposal::TransactionType;
use crate::transaction::{execute_for_summary, generate_salt, word_to_hex};
const NEW_GUARDIAN_COMMITMENT: [u32; 4] = [11, 12, 13, 14];
async fn client_for_scheme(
dir: &std::path::Path,
scheme: SignatureScheme,
seed: u8,
) -> (MultisigClient, miden_protocol::account::Account) {
let keystore: Arc<dyn KeyManager> = match scheme {
SignatureScheme::Falcon => Arc::new(GuardianKeyStore::generate()),
SignatureScheme::Ecdsa => Arc::new(EcdsaGuardianKeyStore::generate()),
};
let account = multisig_account_with_scheme(
keystore.commitment(),
Word::from([9u32, 9, 9, 9]),
seed,
scheme,
);
let api = chain_with_notes(vec![]);
let (mut client, _store) =
offline_client_parts_with_keystore(dir, api.clone(), None, keystore).await;
client.set_node_rpc_client(api.clone());
client.add_or_update_account(&account, false).await.unwrap();
client.account = Some(MultisigAccount::new(account.clone()));
client.miden_client.sync_state().await.unwrap();
(client, account)
}
async fn switch_target() -> (String, guardian_client::testing::mocks::MockGuardianHandle) {
let service = MockGuardianService::default();
let handle = service.handle();
let endpoint = start_mock_server(service).await.unwrap();
handle.set_persistent_get_pubkey(word_to_hex(&Word::from(NEW_GUARDIAN_COMMITMENT)));
(endpoint, handle)
}
fn assert_queried_only(schemes: &[Option<String>], expected: SignatureScheme, site: &str) {
assert!(
!schemes.is_empty(),
"{site} must check the endpoint commitment"
);
for scheme in schemes {
assert_eq!(
scheme.as_deref(),
Some(expected.as_str()),
"{site} must query GUARDIAN's {} identity, not the server default; got {schemes:?}",
expected.as_str()
);
}
}
async fn offline_creation_queries(scheme: SignatureScheme, seed: u8) -> Vec<Option<String>> {
let dir = tempfile::tempdir().unwrap();
let (mut client, _account) = client_for_scheme(dir.path(), scheme, seed).await;
let (endpoint, handle) = switch_target().await;
client
.create_proposal_offline(TransactionType::switch_guardian(
endpoint,
Word::from(NEW_GUARDIAN_COMMITMENT),
))
.await
.expect("switch proposal is created");
handle.get_pubkey_schemes()
}
async fn online_creation_queries(scheme: SignatureScheme, seed: u8) -> Vec<Option<String>> {
let dir = tempfile::tempdir().unwrap();
let (mut client, account) = client_for_scheme(dir.path(), scheme, seed).await;
let (target_endpoint, target_handle) = switch_target().await;
let current = MockGuardianService::default();
let current_handle = current.handle();
let current_endpoint = start_mock_server(current).await.unwrap();
current_handle.set_persistent_get_state(registered_state(&account));
client
.set_guardian_endpoint(¤t_endpoint, false)
.await
.unwrap();
let _ = client
.propose_transaction(TransactionType::switch_guardian(
target_endpoint,
Word::from(NEW_GUARDIAN_COMMITMENT),
))
.await;
target_handle.get_pubkey_schemes()
}
async fn finalization_queries(scheme: SignatureScheme, seed: u8) -> Vec<Option<String>> {
let dir = tempfile::tempdir().unwrap();
let (mut client, account) = client_for_scheme(dir.path(), scheme, seed).await;
let (endpoint, handle) = switch_target().await;
let tx_type = TransactionType::switch_guardian(endpoint, Word::from(NEW_GUARDIAN_COMMITMENT));
let auth_args = client
.multisig_auth_args(generate_salt(), None, None)
.await
.unwrap();
let tx_request = build_final_transaction_request(
&client.miden_client,
&tx_type,
&account,
&auth_args,
Vec::new(),
None,
Some(&[]),
scheme,
)
.await
.expect("switch request builds");
execute_for_summary(&mut client.miden_client, account.id(), tx_request.clone())
.await
.expect("switch request executes for a summary");
let _ = client
.finalize_transaction(account.id(), tx_request, &tx_type)
.await;
handle.get_pubkey_schemes()
}
#[tokio::test]
async fn offline_switch_creation_queries_the_falcon_identity() {
let schemes = offline_creation_queries(SignatureScheme::Falcon, 61).await;
assert_queried_only(&schemes, SignatureScheme::Falcon, "offline creation");
}
#[tokio::test]
async fn offline_switch_creation_queries_the_ecdsa_identity() {
let schemes = offline_creation_queries(SignatureScheme::Ecdsa, 67).await;
assert_queried_only(&schemes, SignatureScheme::Ecdsa, "offline creation");
}
#[tokio::test]
async fn online_switch_creation_queries_the_falcon_identity() {
let schemes = online_creation_queries(SignatureScheme::Falcon, 71).await;
assert_queried_only(&schemes, SignatureScheme::Falcon, "online creation");
}
#[tokio::test]
async fn online_switch_creation_queries_the_ecdsa_identity() {
let schemes = online_creation_queries(SignatureScheme::Ecdsa, 73).await;
assert_queried_only(&schemes, SignatureScheme::Ecdsa, "online creation");
}
#[tokio::test]
async fn switch_finalization_queries_the_falcon_identity() {
let schemes = finalization_queries(SignatureScheme::Falcon, 79).await;
assert_queried_only(&schemes, SignatureScheme::Falcon, "finalization");
}
#[tokio::test]
async fn switch_finalization_queries_the_ecdsa_identity() {
let schemes = finalization_queries(SignatureScheme::Ecdsa, 83).await;
assert_queried_only(&schemes, SignatureScheme::Ecdsa, "finalization");
}