clementine_core/rpc/
ecdsa_verification_sig.rs1use alloy::primitives::PrimitiveSignature;
12use alloy::sol_types::Eip712Domain;
13use bitcoin::hashes::Hash;
14use bitcoin::{taproot, OutPoint};
15use bitcoin::{Amount, ScriptBuf};
16use eyre::{Context, Result};
17
18use crate::errors::BridgeError;
19
20alloy_sol_types::sol! {
21 #[derive(Debug)]
22 struct OptimisticPayoutMessage {
23 uint32 withdrawal_id;
24 bytes input_signature;
25 bytes32 input_outpoint_txid;
26 uint32 input_outpoint_vout;
27 bytes output_script_pubkey;
28 uint64 output_amount;
29 }
30
31 #[derive(Debug)]
32 struct OperatorWithdrawalMessage {
33 uint32 withdrawal_id;
34 bytes input_signature;
35 bytes32 input_outpoint_txid;
36 uint32 input_outpoint_vout;
37 bytes output_script_pubkey;
38 uint64 output_amount;
39 }
40}
41
42pub static CLEMENTINE_EIP712_DOMAIN: Eip712Domain = alloy_sol_types::eip712_domain! {
43 name: "ClementineVerification",
44 version: "1",
45};
46
47pub trait WithdrawalMessage {
48 fn new(
49 deposit_id: u32,
50 input_signature: taproot::Signature,
51 input_outpoint: OutPoint,
52 output_script_pubkey: ScriptBuf,
53 output_amount: Amount,
54 ) -> Self;
55}
56
57impl WithdrawalMessage for OptimisticPayoutMessage {
58 fn new(
59 deposit_id: u32,
60 input_signature: taproot::Signature,
61 input_outpoint: OutPoint,
62 output_script_pubkey: ScriptBuf,
63 output_amount: Amount,
64 ) -> Self {
65 OptimisticPayoutMessage {
66 withdrawal_id: deposit_id,
67 input_signature: input_signature.serialize().to_vec().into(),
68 input_outpoint_txid: input_outpoint.txid.to_byte_array().into(),
69 input_outpoint_vout: input_outpoint.vout,
70 output_script_pubkey: output_script_pubkey.as_bytes().to_vec().into(),
71 output_amount: output_amount.to_sat(),
72 }
73 }
74}
75
76impl WithdrawalMessage for OperatorWithdrawalMessage {
77 fn new(
78 deposit_id: u32,
79 input_signature: taproot::Signature,
80 input_outpoint: OutPoint,
81 output_script_pubkey: ScriptBuf,
82 output_amount: Amount,
83 ) -> Self {
84 OperatorWithdrawalMessage {
85 withdrawal_id: deposit_id,
86 input_signature: input_signature.serialize().to_vec().into(),
87 input_outpoint_txid: input_outpoint.txid.to_byte_array().into(),
88 input_outpoint_vout: input_outpoint.vout,
89 output_script_pubkey: output_script_pubkey.as_bytes().to_vec().into(),
90 output_amount: output_amount.to_sat(),
91 }
92 }
93}
94
95pub fn recover_address_from_ecdsa_signature<M: WithdrawalMessage + alloy_sol_types::SolStruct>(
110 deposit_id: u32,
111 input_signature: taproot::Signature,
112 input_outpoint: OutPoint,
113 output_script_pubkey: ScriptBuf,
114 output_amount: Amount,
115 signature: PrimitiveSignature,
116) -> Result<alloy::primitives::Address, BridgeError> {
117 let params = M::new(
118 deposit_id,
119 input_signature,
120 input_outpoint,
121 output_script_pubkey,
122 output_amount,
123 );
124
125 let eip712_hash = params.eip712_signing_hash(&CLEMENTINE_EIP712_DOMAIN);
126
127 let address = signature
128 .recover_address_from_prehash(&eip712_hash)
129 .wrap_err("Invalid signature")?;
130 Ok(address)
131}