clementine_core/rpc/parser/
operator.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::{
    citrea::CitreaClientT,
    fetch_next_message_from_stream,
    operator::Operator,
    rpc::{
        clementine::{
            operator_params, DepositParams, DepositSignSession, OperatorConfig, OperatorParams,
            Outpoint, SchnorrSig, WithdrawParams,
        },
        error::{self, expected_msg_got_none},
    },
};
use bitcoin::{
    address::NetworkUnchecked, secp256k1::schnorr::Signature, Address, Amount, OutPoint, ScriptBuf,
    XOnlyPublicKey,
};
use bitvm::signatures::winternitz;
use std::str::FromStr;
use tonic::Status;

impl<C> From<Operator<C>> for OperatorParams
where
    C: CitreaClientT,
{
    fn from(operator: Operator<C>) -> Self {
        let operator_config = OperatorConfig {
            collateral_funding_outpoint: Some(Outpoint {
                txid: Some(operator.collateral_funding_outpoint.txid.into()),
                vout: operator.collateral_funding_outpoint.vout,
            }),
            xonly_pk: operator.signer.xonly_public_key.to_string(),
            wallet_reimburse_address: operator.reimburse_addr.to_string(),
        };

        OperatorParams {
            response: Some(operator_params::Response::OperatorDetails(operator_config)),
        }
    }
}

impl From<winternitz::PublicKey> for OperatorParams {
    fn from(winternitz_pubkey: winternitz::PublicKey) -> Self {
        OperatorParams {
            response: Some(operator_params::Response::WinternitzPubkeys(
                winternitz_pubkey.into(),
            )),
        }
    }
}

impl From<Signature> for OperatorParams {
    fn from(sig: Signature) -> Self {
        OperatorParams {
            response: Some(operator_params::Response::UnspentKickoffSig(SchnorrSig {
                schnorr_sig: sig.serialize().to_vec(),
            })),
        }
    }
}

impl TryFrom<DepositSignSession> for DepositParams {
    type Error = Status;

    fn try_from(deposit_sign_session: DepositSignSession) -> Result<Self, Self::Error> {
        match deposit_sign_session.deposit_params {
            Some(deposit_params) => Ok(deposit_params),
            None => Err(expected_msg_got_none("Deposit Params")()),
        }
    }
}

/// Parses operator configuration from a given stream.
///
/// # Returns
///
/// A tuple, containing:
///
/// - Operator index
/// - Collateral Funding txid
/// - Operator's X-only public key
/// - Wallet reimburse address
pub async fn parse_details(
    stream: &mut tonic::Streaming<OperatorParams>,
) -> Result<(OutPoint, XOnlyPublicKey, Address<NetworkUnchecked>), Status> {
    let operator_param = fetch_next_message_from_stream!(stream, response)?;

    let operator_config =
        if let operator_params::Response::OperatorDetails(operator_config) = operator_param {
            operator_config
        } else {
            return Err(expected_msg_got_none("OperatorDetails")());
        };

    let operator_xonly_pk = XOnlyPublicKey::from_str(&operator_config.xonly_pk)
        .map_err(|_| Status::invalid_argument("Invalid operator xonly public key".to_string()))?;

    let collateral_funding_outpoint = operator_config
        .collateral_funding_outpoint
        .ok_or(Status::invalid_argument(
            "Collateral funding outpoint not provided".to_string(),
        ))?
        .try_into()?;

    let wallet_reimburse_address = Address::from_str(&operator_config.wallet_reimburse_address)
        .map_err(|e| {
            Status::invalid_argument(format!("Failed to parse wallet reimburse address: {:?}", e))
        })?;

    Ok((
        collateral_funding_outpoint,
        operator_xonly_pk,
        wallet_reimburse_address,
    ))
}

pub async fn parse_winternitz_public_keys(
    stream: &mut tonic::Streaming<OperatorParams>,
) -> Result<winternitz::PublicKey, Status> {
    let operator_param = fetch_next_message_from_stream!(stream, response)?;

    if let operator_params::Response::WinternitzPubkeys(wpk) = operator_param {
        Ok(wpk.try_into()?)
    } else {
        Err(expected_msg_got_none("WinternitzPubkeys")())
    }
}

pub async fn parse_schnorr_sig(
    stream: &mut tonic::Streaming<OperatorParams>,
) -> Result<Signature, Status> {
    let operator_param = fetch_next_message_from_stream!(stream, response)?;

    if let operator_params::Response::UnspentKickoffSig(wpk) = operator_param {
        Ok(wpk.try_into()?)
    } else {
        Err(expected_msg_got_none("WinternitzPubkeys")())
    }
}

pub async fn parse_withdrawal_sig_params(
    params: WithdrawParams,
) -> Result<(u32, Signature, OutPoint, ScriptBuf, Amount), Status> {
    let input_signature = Signature::from_slice(&params.input_signature)
        .map_err(|e| error::invalid_argument("user_sig", "Can't convert input to Signature")(e))?;

    let input_outpoint: OutPoint = params
        .input_outpoint
        .ok_or_else(error::input_ended_prematurely)?
        .try_into()?;

    let users_intent_script_pubkey = ScriptBuf::from_bytes(params.output_script_pubkey);

    Ok((
        params.withdrawal_id,
        input_signature,
        input_outpoint,
        users_intent_script_pubkey,
        Amount::from_sat(params.output_amount),
    ))
}