clementine_tx_sender/citrea/
mod.rs1pub mod data_serialization;
5mod reveal_scripts;
6pub mod sync;
7
8#[cfg(all(test, feature = "citrea", feature = "testing"))]
9mod tests;
10
11use bitcoin::absolute::LockTime;
12use bitcoin::blockdata::script;
13use bitcoin::secp256k1::{Message, PublicKey, SecretKey};
14use bitcoin::{Address, OutPoint, Sequence, Transaction, TxIn, TxOut, Txid, Witness};
15use clementine_primitives::MIN_TAPROOT_AMOUNT;
16use sha2::{Digest, Sha256};
17
18use crate::signer::SECP;
19pub use tx_sender_types::CitreaTxRequest;
20
21pub(crate) const MAX_CHUNK_SIZE: u32 = 390_000;
28
29#[derive(Debug, Clone, Copy)]
33#[repr(u16)]
34pub enum TransactionKind {
35 Complete = 0,
37 Aggregate = 1,
39 Chunks = 2,
41 BatchProofMethodId = 3,
43 SequencerCommitment = 4,
45 Unknown(u16),
49}
50
51impl TransactionKind {
52 fn to_bytes(self) -> [u8; 2] {
54 match self {
55 TransactionKind::Complete => 0u16.to_le_bytes(),
56 TransactionKind::Aggregate => 1u16.to_le_bytes(),
57 TransactionKind::Chunks => 2u16.to_le_bytes(),
58 TransactionKind::BatchProofMethodId => 3u16.to_le_bytes(),
59 TransactionKind::SequencerCommitment => 4u16.to_le_bytes(),
60 TransactionKind::Unknown(n) => n.to_le_bytes(),
61 }
62 }
63
64 pub(crate) fn from_u16(value: u16) -> TransactionKind {
66 match value {
67 0 => TransactionKind::Complete,
68 1 => TransactionKind::Aggregate,
69 2 => TransactionKind::Chunks,
70 3 => TransactionKind::BatchProofMethodId,
71 4 => TransactionKind::SequencerCommitment,
72 n => TransactionKind::Unknown(n),
73 }
74 }
75
76 pub(crate) fn as_i16(&self) -> i16 {
78 match self {
79 TransactionKind::Complete => 0,
80 TransactionKind::Aggregate => 1,
81 TransactionKind::Chunks => 2,
82 TransactionKind::BatchProofMethodId => 3,
83 TransactionKind::SequencerCommitment => 4,
84 TransactionKind::Unknown(n) => *n as i16,
85 }
86 }
87}
88
89pub(crate) fn build_commit_transaction(recipients: &[Address]) -> Transaction {
92 let outputs = recipients
93 .iter()
94 .map(|recipient| TxOut {
95 value: MIN_TAPROOT_AMOUNT,
96 script_pubkey: recipient.script_pubkey(),
97 })
98 .collect();
99
100 Transaction {
101 lock_time: LockTime::ZERO,
102 version: bitcoin::transaction::Version(2),
103 input: vec![],
104 output: outputs,
105 }
106}
107
108pub(crate) fn build_reveal_transaction(input_txid: Txid, input_vout: u32) -> Transaction {
110 let inputs = vec![TxIn {
111 previous_output: OutPoint {
112 txid: input_txid,
113 vout: input_vout,
114 },
115 script_sig: script::Builder::new().into_script(),
116 witness: Witness::new(),
117 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
118 }];
119
120 Transaction {
121 lock_time: LockTime::ZERO,
122 version: bitcoin::transaction::Version(2),
123 input: inputs,
124 output: vec![],
125 }
126}
127
128pub fn sign_blob_with_private_key(blob: &[u8], private_key: &SecretKey) -> (Vec<u8>, Vec<u8>) {
131 let message = calculate_sha256(blob);
132 let public_key = PublicKey::from_secret_key(&SECP, private_key);
133 let msg = Message::from_digest(message);
134 let sig = SECP.sign_ecdsa(&msg, private_key);
135 (
136 sig.serialize_compact().to_vec(),
137 public_key.serialize().to_vec(),
138 )
139}
140
141pub(crate) fn calculate_sha256(input: &[u8]) -> [u8; 32] {
142 let mut hasher = Sha256::default();
143 hasher.update(input);
144 hasher.finalize().into()
145}