spark_sdk/wallet/internal_handlers/
utils.rs

1// crate
2use crate::constants::spark::TIME_LOCK_INTERVAL;
3use crate::error::SparkSdkError;
4
5// external spark crates
6use spark_protos::common::SigningCommitment as ProtoSigningCommitment;
7
8// crate::common_types
9use crate::common_types::types::Transaction;
10
11// external crates
12use bitcoin::consensus::{self, Encodable};
13use frost_secp256k1_tr::round1::SigningCommitments as FrostSigningCommitments;
14
15pub(crate) fn frost_commitment_to_proto_commitment(
16    commitments: &FrostSigningCommitments,
17) -> Result<ProtoSigningCommitment, SparkSdkError> {
18    let hiding = commitments.hiding().serialize().unwrap();
19    let binding = commitments.binding().serialize().unwrap();
20
21    Ok(ProtoSigningCommitment { hiding, binding })
22}
23
24pub(crate) fn serialize_bitcoin_transaction(
25    transaction: &Transaction,
26) -> Result<Vec<u8>, SparkSdkError> {
27    let mut buf = Vec::new();
28    transaction.consensus_encode(&mut buf).map_err(|e| {
29        SparkSdkError::InvalidBitcoinTransaction(format!(
30            "Failed to serialize Bitcoin transaction: {}",
31            e
32        ))
33    })?;
34
35    Ok(buf)
36}
37
38pub(crate) fn bitcoin_tx_from_bytes(
39    transaction_bytes: &Vec<u8>,
40) -> Result<Transaction, SparkSdkError> {
41    if transaction_bytes.len() == 0 {
42        return Err(SparkSdkError::InvalidBitcoinTransaction(
43            "Cannot deserialize Bitcoin transaction: buffer is empty".to_string(),
44        ));
45    }
46
47    let transaction = consensus::deserialize(transaction_bytes)
48        .map_err(|_| SparkSdkError::InvalidBitcoinTransaction("Invalid transaction".to_string()))?;
49
50    Ok(transaction)
51}
52
53pub(crate) fn next_sequence(curr_sequence: u32) -> u32 {
54    let mask = curr_sequence & 0xFFFF;
55    if TIME_LOCK_INTERVAL >= mask {
56        return 0;
57    };
58
59    (1 << 30) | (mask - TIME_LOCK_INTERVAL)
60}
61
62pub(crate) mod parsers {
63
64    use crate::common_types::types::PublicKey;
65    use crate::common_types::types::SecretKey;
66    use crate::error::SparkSdkError;
67
68    pub fn parse_secret_key(bytes: &Vec<u8>) -> Result<SecretKey, SparkSdkError> {
69        let secret_key = bitcoin::secp256k1::SecretKey::from_slice(bytes).map_err(|e| {
70            SparkSdkError::InvalidArgument(format!("Private key is not valid: {}", e))
71        })?;
72
73        Ok(secret_key)
74    }
75
76    pub fn parse_public_key(bytes: &Vec<u8>) -> Result<PublicKey, SparkSdkError> {
77        let public_key = bitcoin::secp256k1::PublicKey::from_slice(bytes).map_err(|e| {
78            SparkSdkError::InvalidArgument(format!("Public key is not valid: {}", e))
79        })?;
80
81        Ok(public_key)
82    }
83}
84
85#[cfg(test)]
86mod next_sequence_tests {
87    use super::*;
88    use crate::constants::spark::TIME_LOCK_INTERVAL;
89
90    #[test]
91    fn test_next_sequence_returns_zero_when_timelock_greater_than_mask() {
92        let curr_sequence = TIME_LOCK_INTERVAL;
93        assert_eq!(next_sequence(curr_sequence), 0);
94    }
95
96    #[test]
97    fn test_next_sequence_returns_zero_when_timelock_equals_mask() {
98        let curr_sequence = TIME_LOCK_INTERVAL & 0xFFFF;
99        assert_eq!(next_sequence(curr_sequence), 0);
100    }
101
102    #[test]
103    fn test_next_sequence_decrements_by_timelock_interval() {
104        let curr_sequence = 100;
105        let expected = (1 << 30) | (100 - TIME_LOCK_INTERVAL);
106        assert_eq!(next_sequence(curr_sequence), expected);
107    }
108
109    #[test]
110    fn test_next_sequence_only_uses_lower_16_bits_of_input() {
111        let curr_sequence = 0xFFFFFFFF;
112        let mask = 0xFFFF;
113        let expected = (1 << 30) | ((mask - TIME_LOCK_INTERVAL) & 0xFFFF);
114        assert_eq!(next_sequence(curr_sequence), expected);
115    }
116}