1
2pub extern crate bitcoin;
3
4#[macro_use] extern crate serde;
5#[macro_use] extern crate lazy_static;
6
7#[macro_use] pub mod util;
8
9pub mod address;
10pub mod arkoor;
11pub mod attestations;
12pub mod board;
13pub mod connectors;
14pub mod encode;
15pub mod error;
16pub mod fees;
17pub mod forfeit;
18pub mod lightning;
19pub mod mailbox;
20pub mod musig;
21pub mod offboard;
22pub mod recovery;
23pub mod rounds;
24pub mod message;
25pub mod tree;
26pub mod vtxo;
27pub mod integration;
28
29pub use crate::address::Address;
30pub use crate::encode::{ProtocolEncoding, WriteExt, ReadExt, ProtocolDecodingError};
31pub use crate::vtxo::{Vtxo, VtxoId, VtxoPolicy, ServerVtxoPolicy, ServerVtxo};
32
33#[cfg(test)]
34mod napkin;
35#[cfg(any(test, feature = "test-util"))]
36pub mod test_util;
37
38
39use std::time::Duration;
40
41use bitcoin::{Amount, FeeRate, Network};
42use bitcoin::secp256k1::{self, PublicKey};
43
44use bitcoin_ext::BlockDelta;
45
46use crate::fees::FeeSchedule;
47
48lazy_static! {
49 pub static ref SECP: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct ArkInfo {
55 pub network: Network,
57 pub server_pubkey: PublicKey,
59 pub mailbox_pubkey: PublicKey,
61 pub round_interval: Duration,
63 pub nb_round_nonces: usize,
65 pub vtxo_exit_delta: BlockDelta,
67 pub vtxo_lifetime: BlockDelta,
69 pub htlc_send_expiry_delta: BlockDelta,
71 pub htlc_expiry_delta: BlockDelta,
73 pub max_vtxo_amount: Option<Amount>,
75 pub required_board_confirmations: usize,
77 pub max_user_invoice_cltv_delta: BlockDelta,
80 pub min_board_amount: Amount,
82
83 #[deprecated(note = "renamed to vtxo_lifetime")]
89 pub vtxo_expiry_delta: BlockDelta,
90
91 #[deprecated(since = "0.1.5", note = "use ServerConnection::offboard_feerate instead")]
99 pub offboard_feerate: FeeRate,
100
101 pub max_offboard_inputs: usize,
103
104 pub ln_receive_anti_dos_required: bool,
108
109 pub fees: FeeSchedule,
111
112 pub max_vtxo_exit_depth: u16,
117
118 pub tos_link: Option<String>,
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
124pub struct VtxoRequest {
125 pub amount: Amount,
126 #[serde(with = "crate::encode::serde")]
127 pub policy: VtxoPolicy,
128}
129
130impl AsRef<VtxoRequest> for VtxoRequest {
131 fn as_ref(&self) -> &VtxoRequest {
132 self
133 }
134}
135
136#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
138pub struct SignedVtxoRequest {
139 pub vtxo: VtxoRequest,
141 pub cosign_pubkey: PublicKey,
144 pub nonces: Vec<musig::PublicNonce>,
146}
147
148impl AsRef<VtxoRequest> for SignedVtxoRequest {
149 fn as_ref(&self) -> &VtxoRequest {
150 &self.vtxo
151 }
152}
153
154pub mod scripts {
155 use bitcoin::{opcodes, ScriptBuf, TapSighash, TapTweakHash, Transaction};
156 use bitcoin::hashes::{sha256, ripemd160, Hash};
157 use bitcoin::secp256k1::{schnorr, PublicKey, XOnlyPublicKey};
158
159 use bitcoin_ext::{BlockDelta, BlockHeight, TAPROOT_KEYSPEND_WEIGHT};
160
161 use crate::musig;
162
163 pub fn delayed_sign(delay_blocks: BlockDelta, pubkey: XOnlyPublicKey) -> ScriptBuf {
165 let csv = bitcoin::Sequence::from(delay_blocks);
166 bitcoin::Script::builder()
167 .push_int(csv.to_consensus_u32() as i64)
168 .push_opcode(opcodes::all::OP_CSV)
169 .push_opcode(opcodes::all::OP_DROP)
170 .push_x_only_key(&pubkey)
171 .push_opcode(opcodes::all::OP_CHECKSIG)
172 .into_script()
173 }
174
175 pub fn timelock_sign(timelock_height: BlockHeight, pubkey: XOnlyPublicKey) -> ScriptBuf {
177 let lt = timelock_height.to_locktime().unwrap();
178 bitcoin::Script::builder()
179 .push_int(lt.to_consensus_u32() as i64)
180 .push_opcode(opcodes::all::OP_CLTV)
181 .push_opcode(opcodes::all::OP_DROP)
182 .push_x_only_key(&pubkey)
183 .push_opcode(opcodes::all::OP_CHECKSIG)
184 .into_script()
185 }
186
187 pub fn delay_timelock_sign(
189 delay_blocks: BlockDelta,
190 timelock_height: BlockHeight,
191 pubkey: XOnlyPublicKey,
192 ) -> ScriptBuf {
193 let csv = bitcoin::Sequence::from(delay_blocks);
194 let lt = timelock_height.to_locktime().unwrap();
195 bitcoin::Script::builder()
196 .push_int(lt.to_consensus_u32().try_into().unwrap())
197 .push_opcode(opcodes::all::OP_CLTV)
198 .push_opcode(opcodes::all::OP_DROP)
199 .push_int(csv.to_consensus_u32().try_into().unwrap())
200 .push_opcode(opcodes::all::OP_CSV)
201 .push_opcode(opcodes::all::OP_DROP)
202 .push_x_only_key(&pubkey)
203 .push_opcode(opcodes::all::OP_CHECKSIG)
204 .into_script()
205 }
206
207 pub fn hash_and_sign(hash: sha256::Hash, pubkey: XOnlyPublicKey) -> ScriptBuf {
213 let hash_160 = ripemd160::Hash::hash(&hash[..]);
214
215 bitcoin::Script::builder()
216 .push_opcode(opcodes::all::OP_SIZE)
217 .push_int(32)
218 .push_opcode(opcodes::all::OP_EQUALVERIFY)
219 .push_opcode(opcodes::all::OP_HASH160)
220 .push_slice(hash_160.as_byte_array())
221 .push_opcode(opcodes::all::OP_EQUALVERIFY)
222 .push_x_only_key(&pubkey)
223 .push_opcode(opcodes::all::OP_CHECKSIG)
224 .into_script()
225 }
226
227 pub fn hash_and_sign_v0(hash: sha256::Hash, pubkey: XOnlyPublicKey) -> ScriptBuf {
233 let hash_160 = ripemd160::Hash::hash(&hash[..]);
234
235 bitcoin::Script::builder()
236 .push_opcode(opcodes::all::OP_HASH160)
237 .push_slice(hash_160.as_byte_array())
238 .push_opcode(opcodes::all::OP_EQUALVERIFY)
239 .push_x_only_key(&pubkey)
240 .push_opcode(opcodes::all::OP_CHECKSIG)
241 .into_script()
242 }
243
244 pub fn hash_delay_sign(
245 hash: sha256::Hash,
246 delay_blocks: BlockDelta,
247 pubkey: XOnlyPublicKey,
248 ) -> ScriptBuf {
249 let hash_160 = ripemd160::Hash::hash(&hash[..]);
250 let csv = bitcoin::Sequence::from(delay_blocks);
251
252 bitcoin::Script::builder()
253 .push_int(csv.to_consensus_u32().try_into().unwrap())
254 .push_opcode(opcodes::all::OP_CSV)
255 .push_opcode(opcodes::all::OP_DROP)
256 .push_opcode(opcodes::all::OP_SIZE)
257 .push_int(32)
258 .push_opcode(opcodes::all::OP_EQUALVERIFY)
259 .push_opcode(opcodes::all::OP_HASH160)
260 .push_slice(hash_160.as_byte_array())
261 .push_opcode(opcodes::all::OP_EQUALVERIFY)
262 .push_x_only_key(&pubkey)
263 .push_opcode(opcodes::all::OP_CHECKSIG)
264 .into_script()
265 }
266
267 pub fn hash_delay_sign_v0(
268 hash: sha256::Hash,
269 delay_blocks: BlockDelta,
270 pubkey: XOnlyPublicKey,
271 ) -> ScriptBuf {
272 let hash_160 = ripemd160::Hash::hash(&hash[..]);
273 let csv = bitcoin::Sequence::from(delay_blocks);
274
275 bitcoin::Script::builder()
276 .push_int(csv.to_consensus_u32().try_into().unwrap())
277 .push_opcode(opcodes::all::OP_CSV)
278 .push_opcode(opcodes::all::OP_DROP)
279 .push_opcode(opcodes::all::OP_HASH160)
280 .push_slice(hash_160.as_byte_array())
281 .push_opcode(opcodes::all::OP_EQUALVERIFY)
282 .push_x_only_key(&pubkey)
283 .push_opcode(opcodes::all::OP_CHECKSIG)
284 .into_script()
285 }
286
287 pub fn fill_taproot_sigs(tx: &mut Transaction, sigs: &[schnorr::Signature]) {
292 assert_eq!(tx.input.len(), sigs.len());
293 for (input, sig) in tx.input.iter_mut().zip(sigs.iter()) {
294 assert!(input.witness.is_empty());
295 input.witness.push(&sig[..]);
296 debug_assert_eq!(TAPROOT_KEYSPEND_WEIGHT.to_wu(), input.witness.size() as u64);
297 }
298 }
299
300 pub fn verify_partial_sig(
302 sighash: TapSighash,
303 tweak: TapTweakHash,
304 signer: (PublicKey, &musig::PublicNonce),
305 other: (PublicKey, &musig::PublicNonce),
306 partial_signature: &musig::PartialSignature,
307 ) -> bool {
308 let agg_nonce = musig::nonce_agg(&[&signer.1, &other.1]);
309 let agg_pk = musig::tweaked_key_agg([signer.0, other.0], tweak.to_byte_array()).0;
310
311 let session = musig::Session::new(&agg_pk, agg_nonce, &sighash.to_byte_array());
312 session.partial_verify(
313 &agg_pk, partial_signature, signer.1, musig::pubkey_to(signer.0),
314 )
315 }
316}