spark_sdk/wallet/internal_handlers/implementations/
deposit.rs

1use crate::error::SparkSdkError;
2use crate::signer::traits::SparkSigner;
3use crate::wallet::client::SparkSdk;
4use crate::wallet::internal_handlers::traits::deposit::DepositInternalHandlers;
5use crate::wallet::utils::bitcoin::compute_taproot_key_no_script;
6use crate::wallet::utils::ecdsa::verify_operator_ecdsa_signature;
7use crate::wallet::utils::proof_of_possession::proof_of_possession_message_hash_for_deposit_address;
8use sha256;
9use tonic::async_trait;
10
11#[async_trait]
12impl<S: SparkSigner + Send + Sync + Clone + 'static> DepositInternalHandlers<S> for SparkSdk<S> {
13    async fn validate_deposit_address(
14        &self,
15        address: spark_protos::spark::Address,
16        user_pubkey: Vec<u8>,
17    ) -> Result<(), SparkSdkError> {
18        use spark_cryptography::key_arithmetic::subtract_public_keys;
19
20        let deposit_address_proof = match address.deposit_address_proof {
21            Some(deposit_address_proof) => deposit_address_proof,
22            None => {
23                return Err(SparkSdkError::DepositAddressValidationFailed(
24                    "Deposit address proof is empty".to_string(),
25                ));
26            }
27        };
28
29        let se_pubkey = subtract_public_keys(&address.verifying_key, &user_pubkey)?;
30
31        let message = proof_of_possession_message_hash_for_deposit_address(
32            &self.get_identity_public_key(),
33            &se_pubkey,
34            address.address.as_bytes(),
35        );
36
37        let sig = bitcoin::secp256k1::schnorr::Signature::from_slice(
38            &deposit_address_proof.proof_of_possession_signature,
39        )?;
40
41        let se_pubkey = bitcoin::secp256k1::PublicKey::from_slice(&se_pubkey)?;
42        let taproot_key = compute_taproot_key_no_script(se_pubkey)?;
43
44        let secp = bitcoin::secp256k1::Secp256k1::new();
45        let verified = secp.verify_schnorr(
46            &sig,
47            &bitcoin::secp256k1::Message::from_digest_slice(&message)?,
48            // &bitcoin::XOnlyPublicKey::from_slice(&se_pubkey.serialize())?,
49            &taproot_key,
50        );
51
52        if verified.is_err() {
53            return Err(SparkSdkError::DepositAddressValidationFailed(
54                "signature verification failed".to_string(),
55            ));
56        }
57
58        let addr_hash = sha256::digest(address.address.as_bytes());
59        let secp = bitcoin::secp256k1::Secp256k1::verification_only();
60        for operator in self.config.spark_config.spark_operators.iter() {
61            if operator.id == self.config.spark_config.coordinator_index {
62                continue;
63            }
64
65            let operator_sig = deposit_address_proof
66                .address_signatures
67                .get(&operator.frost_identifier)
68                .ok_or_else(|| {
69                    SparkSdkError::DepositAddressValidationFailed(format!(
70                        "Missing signature for operator {}",
71                        operator.frost_identifier
72                    ))
73                })?;
74
75            // Convert hex string addr_hash to bytes
76            let addr_hash_bytes = hex::decode(&addr_hash).map_err(|e| {
77                SparkSdkError::DepositAddressValidationFailed(format!(
78                    "Failed to decode addr_hash: {}",
79                    e
80                ))
81            })?;
82
83            verify_operator_ecdsa_signature(
84                operator_sig,
85                &operator.identity_public_key,
86                &addr_hash_bytes,
87                &secp,
88            )?;
89        }
90
91        Ok(())
92    }
93}