miden-multisig-client 0.14.6

High-level SDK for interacting with multisig accounts on Miden via GUARDIAN
Documentation
use guardian_client::GuardianClient;
use miden_protocol::Word;

use crate::error::{MultisigError, Result};
use crate::keystore::word_from_hex;
use crate::transaction::word_to_hex;

pub(crate) async fn verify_endpoint_commitment(
    endpoint: &str,
    expected_commitment: Word,
) -> Result<()> {
    let mut client = GuardianClient::connect(endpoint).await.map_err(|e| {
        MultisigError::GuardianConnection(format!(
            "failed to connect to GUARDIAN endpoint {}: {}",
            endpoint, e
        ))
    })?;

    let (endpoint_commitment_hex, _raw_pubkey) = client.get_pubkey(None).await.map_err(|e| {
        MultisigError::GuardianServer(format!(
            "failed to get pubkey from GUARDIAN endpoint {}: {}",
            endpoint, e
        ))
    })?;

    let endpoint_commitment =
        word_from_hex(&endpoint_commitment_hex).map_err(MultisigError::HexDecode)?;

    ensure_commitment_match(endpoint, expected_commitment, endpoint_commitment)
}

pub(crate) fn ensure_commitment_match(
    endpoint: &str,
    expected_commitment: Word,
    endpoint_commitment: Word,
) -> Result<()> {
    if endpoint_commitment == expected_commitment {
        return Ok(());
    }

    Err(MultisigError::InvalidConfig(format!(
        "refusing to use GUARDIAN endpoint {}: endpoint pubkey commitment {} does not match expected {}",
        endpoint,
        word_to_hex(&endpoint_commitment),
        word_to_hex(&expected_commitment)
    )))
}

#[cfg(test)]
mod tests {
    use super::*;
    use miden_protocol::Word;

    #[test]
    fn ensure_commitment_match_accepts_equal_commitments() {
        let commitment = Word::from([1u32, 2, 3, 4]);
        let result = ensure_commitment_match("http://localhost:50051", commitment, commitment);
        assert!(result.is_ok());
    }

    #[test]
    fn ensure_commitment_match_rejects_mismatch() {
        let expected = Word::from([1u32, 2, 3, 4]);
        let actual = Word::from([5u32, 6, 7, 8]);
        let error = ensure_commitment_match("http://localhost:50051", expected, actual)
            .expect_err("expected mismatch error");

        let message = error.to_string();
        assert!(message.contains("refusing to use GUARDIAN endpoint"));
        assert!(message.contains("does not match expected"));
    }
}