1use bitcoin::sighash::SighashCache;
4use bitcoin::{sighash::EcdsaSighashType, Script, Transaction, TxOut};
5use bitcoin::{Amount, ScriptBuf, Sequence, Witness};
6use bitcoin::{PubkeyHash, WitnessProgram, WitnessVersion};
7use secp256k1_zkp::{ecdsa::Signature, Message, PublicKey, Secp256k1, SecretKey, Signing};
8
9use crate::Error;
10
11pub(crate) const DISABLE_LOCKTIME: Sequence = Sequence(0xffffffff);
14pub(crate) const ENABLE_LOCKTIME: Sequence = Sequence(0xfffffffe);
17
18pub(crate) fn get_sig_hash_msg(
22 tx: &Transaction,
23 input_index: usize,
24 script_pubkey: &Script,
25 value: u64,
26) -> Result<Message, Error> {
27 let sig_hash = SighashCache::new(tx).p2wsh_signature_hash(
28 input_index,
29 script_pubkey,
30 Amount::from_sat(value),
31 EcdsaSighashType::All,
32 )?;
33
34 Ok(Message::from_digest_slice(sig_hash.as_ref()).unwrap())
35}
36
37pub(crate) fn finalize_sig(sig: &Signature, sig_hash_type: EcdsaSighashType) -> Vec<u8> {
40 [
41 sig.serialize_der().as_ref(),
42 &[sig_hash_type.to_u32() as u8],
43 ]
44 .concat()
45}
46
47pub fn get_raw_sig_for_tx_input<C: Signing>(
49 secp: &Secp256k1<C>,
50 tx: &Transaction,
51 input_index: usize,
52 script_pubkey: &Script,
53 value: u64,
54 sk: &SecretKey,
55) -> Result<Signature, Error> {
56 let sig_hash_msg = get_sig_hash_msg(tx, input_index, script_pubkey, value)?;
57 Ok(secp.sign_ecdsa_low_r(&sig_hash_msg, sk))
58}
59
60pub fn get_sig_for_tx_input<C: Signing>(
63 secp: &Secp256k1<C>,
64 tx: &Transaction,
65 input_index: usize,
66 script_pubkey: &Script,
67 value: u64,
68 sig_hash_type: EcdsaSighashType,
69 sk: &SecretKey,
70) -> Result<Vec<u8>, Error> {
71 let sig = get_raw_sig_for_tx_input(secp, tx, input_index, script_pubkey, value, sk)?;
72 Ok(finalize_sig(&sig, sig_hash_type))
73}
74
75pub fn get_sig_for_p2wpkh_input<C: Signing>(
77 secp: &Secp256k1<C>,
78 sk: &SecretKey,
79 tx: &Transaction,
80 input_index: usize,
81 value: u64,
82 sig_hash_type: EcdsaSighashType,
83) -> Result<Vec<u8>, Error> {
84 let script_pubkey = get_pkh_script_pubkey_from_sk(secp, sk);
85 get_sig_for_tx_input(
86 secp,
87 tx,
88 input_index,
89 &script_pubkey,
90 value,
91 sig_hash_type,
92 sk,
93 )
94}
95
96pub fn weight_to_fee(weight: usize, fee_rate: u64) -> Result<u64, Error> {
98 (f64::ceil((weight as f64) / 4.0) as u64)
99 .checked_mul(fee_rate)
100 .ok_or(Error::InvalidArgument)
101}
102
103pub fn get_common_fee(fee_rate: u64) -> Result<u64, Error> {
105 let base_weight = crate::FUND_TX_BASE_WEIGHT + crate::CET_BASE_WEIGHT;
106 weight_to_fee(base_weight, fee_rate)
107}
108
109fn get_pkh_script_pubkey_from_sk<C: Signing>(secp: &Secp256k1<C>, sk: &SecretKey) -> ScriptBuf {
110 use bitcoin::hashes::*;
111 let pk = bitcoin::PublicKey {
112 compressed: true,
113 inner: PublicKey::from_secret_key(secp, sk),
114 };
115 let mut hash_engine = PubkeyHash::engine();
116
117 pk.write_into(&mut hash_engine)
118 .expect("Error writing hash.");
119 let pk = PubkeyHash::from_engine(hash_engine);
120
121 ScriptBuf::new_p2pkh(&pk)
122}
123
124pub fn sign_p2wpkh_input<C: Signing>(
127 secp: &Secp256k1<C>,
128 sk: &SecretKey,
129 tx: &mut Transaction,
130 input_index: usize,
131 sig_hash_type: EcdsaSighashType,
132 value: u64,
133) -> Result<(), Error> {
134 tx.input[input_index].witness =
135 get_witness_for_p2wpkh_input(secp, sk, tx, input_index, sig_hash_type, value)?;
136 Ok(())
137}
138
139pub fn get_witness_for_p2wpkh_input<C: Signing>(
141 secp: &Secp256k1<C>,
142 sk: &SecretKey,
143 tx: &Transaction,
144 input_index: usize,
145 sig_hash_type: EcdsaSighashType,
146 value: u64,
147) -> Result<Witness, Error> {
148 let full_sig = get_sig_for_p2wpkh_input(secp, sk, tx, input_index, value, sig_hash_type)?;
149 Ok(Witness::from_slice(&[
150 full_sig,
151 PublicKey::from_secret_key(secp, sk).serialize().to_vec(),
152 ]))
153}
154
155pub fn sign_multi_sig_input<C: Signing>(
160 secp: &Secp256k1<C>,
161 transaction: &mut Transaction,
162 other_sig: &Signature,
163 other_pk: &PublicKey,
164 sk: &SecretKey,
165 script_pubkey: &Script,
166 input_value: u64,
167 input_index: usize,
168) -> Result<(), Error> {
169 let own_sig = get_sig_for_tx_input(
170 secp,
171 transaction,
172 input_index,
173 script_pubkey,
174 input_value,
175 EcdsaSighashType::All,
176 sk,
177 )?;
178
179 let own_pk = &PublicKey::from_secret_key(secp, sk);
180
181 let other_finalized_sig = finalize_sig(other_sig, EcdsaSighashType::All);
182
183 transaction.input[input_index].witness = if own_pk < other_pk {
184 Witness::from_slice(&[
185 Vec::new(),
186 own_sig,
187 other_finalized_sig,
188 script_pubkey.to_bytes(),
189 ])
190 } else {
191 Witness::from_slice(&[
192 Vec::new(),
193 other_finalized_sig,
194 own_sig,
195 script_pubkey.to_bytes(),
196 ])
197 };
198
199 Ok(())
200}
201
202pub(crate) fn redeem_script_to_script_sig(redeem: &Script) -> ScriptBuf {
204 match redeem.len() {
205 0 => ScriptBuf::new(),
206 _ => {
207 let bytes = redeem.as_bytes();
208 ScriptBuf::new_witness_program(&WitnessProgram::new(WitnessVersion::V0, bytes).unwrap())
209 }
210 }
211}
212
213pub(crate) fn order_by_serial_ids<T>(inputs: Vec<T>, ids: &[u64]) -> Vec<T> {
215 debug_assert!(inputs.len() == ids.len());
216 let mut combined: Vec<(&u64, T)> = ids.iter().zip(inputs).collect();
217 combined.sort_by(|a, b| a.0.partial_cmp(b.0).unwrap());
218 combined.into_iter().map(|x| x.1).collect()
219}
220
221pub fn get_output_for_script_pubkey<'a>(
224 tx: &'a Transaction,
225 script_pubkey: &Script,
226) -> Option<(usize, &'a TxOut)> {
227 tx.output
228 .iter()
229 .enumerate()
230 .find(|(_, x)| &x.script_pubkey == script_pubkey)
231}
232
233pub(crate) fn discard_dust(txs: Vec<TxOut>, dust_limit: u64) -> Vec<TxOut> {
235 txs.into_iter()
236 .filter(|x| x.value.to_sat() >= dust_limit)
237 .collect()
238}
239
240pub(crate) fn get_sequence(lock_time: u32) -> Sequence {
241 if lock_time == 0 {
242 DISABLE_LOCKTIME
243 } else {
244 ENABLE_LOCKTIME
245 }
246}
247
248pub(crate) fn compute_var_int_prefix_size(len: usize) -> usize {
249 bitcoin::VarInt(len as u64).size()
250}
251
252pub fn validate_fee_rate(fee_rate_per_vb: u64) -> Result<(), Error> {
254 if fee_rate_per_vb > 25 * 250 {
255 return Err(Error::InvalidArgument);
256 }
257
258 Ok(())
259}