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 rounds;
23pub mod message;
24pub mod tree;
25pub mod vtxo;
26pub mod integration;
27
28pub use crate::address::Address;
29pub use crate::encode::{ProtocolEncoding, WriteExt, ReadExt, ProtocolDecodingError};
30pub use crate::vtxo::{Vtxo, VtxoId, VtxoPolicy, ServerVtxoPolicy, ServerVtxo};
31
32#[cfg(test)]
33mod napkin;
34#[cfg(any(test, feature = "test-util"))]
35pub mod test_util;
36
37
38use std::time::Duration;
39
40use bitcoin::{Amount, FeeRate, Network};
41use bitcoin::secp256k1::{self, PublicKey};
42
43use bitcoin_ext::BlockDelta;
44
45use crate::fees::FeeSchedule;
46
47lazy_static! {
48 pub static ref SECP: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct ArkInfo {
54 pub network: Network,
56 pub server_pubkey: PublicKey,
58 pub mailbox_pubkey: PublicKey,
60 pub round_interval: Duration,
62 pub nb_round_nonces: usize,
64 pub vtxo_exit_delta: BlockDelta,
66 pub vtxo_lifetime: BlockDelta,
68 pub htlc_send_expiry_delta: BlockDelta,
70 pub htlc_expiry_delta: BlockDelta,
72 pub max_vtxo_amount: Option<Amount>,
74 pub required_board_confirmations: usize,
76 pub max_user_invoice_cltv_delta: u16,
79 pub min_board_amount: Amount,
81
82 #[deprecated(note = "renamed to vtxo_lifetime")]
88 pub vtxo_expiry_delta: BlockDelta,
89
90 #[deprecated(since = "0.1.5", note = "use ServerConnection::offboard_feerate instead")]
98 pub offboard_feerate: FeeRate,
99
100 pub max_offboard_inputs: usize,
102
103 pub ln_receive_anti_dos_required: bool,
107
108 pub fees: FeeSchedule,
110
111 pub max_vtxo_exit_depth: u16,
116
117 pub tos_link: Option<String>,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
123pub struct VtxoRequest {
124 pub amount: Amount,
125 #[serde(with = "crate::encode::serde")]
126 pub policy: VtxoPolicy,
127}
128
129impl AsRef<VtxoRequest> for VtxoRequest {
130 fn as_ref(&self) -> &VtxoRequest {
131 self
132 }
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
137pub struct SignedVtxoRequest {
138 pub vtxo: VtxoRequest,
140 pub cosign_pubkey: PublicKey,
143 pub nonces: Vec<musig::PublicNonce>,
145}
146
147impl AsRef<VtxoRequest> for SignedVtxoRequest {
148 fn as_ref(&self) -> &VtxoRequest {
149 &self.vtxo
150 }
151}
152
153pub mod scripts {
154 use bitcoin::{opcodes, ScriptBuf, TapSighash, TapTweakHash, Transaction};
155 use bitcoin::hashes::{sha256, ripemd160, Hash};
156 use bitcoin::secp256k1::{schnorr, PublicKey, XOnlyPublicKey};
157
158 use bitcoin_ext::{BlockDelta, BlockHeight, TAPROOT_KEYSPEND_WEIGHT};
159
160 use crate::musig;
161
162 pub fn delayed_sign(delay_blocks: BlockDelta, pubkey: XOnlyPublicKey) -> ScriptBuf {
164 let csv = bitcoin::Sequence::from_height(delay_blocks);
165 bitcoin::Script::builder()
166 .push_int(csv.to_consensus_u32() as i64)
167 .push_opcode(opcodes::all::OP_CSV)
168 .push_opcode(opcodes::all::OP_DROP)
169 .push_x_only_key(&pubkey)
170 .push_opcode(opcodes::all::OP_CHECKSIG)
171 .into_script()
172 }
173
174 pub fn timelock_sign(timelock_height: BlockHeight, pubkey: XOnlyPublicKey) -> ScriptBuf {
176 let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
177 bitcoin::Script::builder()
178 .push_int(lt.to_consensus_u32() as i64)
179 .push_opcode(opcodes::all::OP_CLTV)
180 .push_opcode(opcodes::all::OP_DROP)
181 .push_x_only_key(&pubkey)
182 .push_opcode(opcodes::all::OP_CHECKSIG)
183 .into_script()
184 }
185
186 pub fn delay_timelock_sign(
188 delay_blocks: BlockDelta,
189 timelock_height: BlockHeight,
190 pubkey: XOnlyPublicKey,
191 ) -> ScriptBuf {
192 let csv = bitcoin::Sequence::from_height(delay_blocks);
193 let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
194 bitcoin::Script::builder()
195 .push_int(lt.to_consensus_u32().try_into().unwrap())
196 .push_opcode(opcodes::all::OP_CLTV)
197 .push_opcode(opcodes::all::OP_DROP)
198 .push_int(csv.to_consensus_u32().try_into().unwrap())
199 .push_opcode(opcodes::all::OP_CSV)
200 .push_opcode(opcodes::all::OP_DROP)
201 .push_x_only_key(&pubkey)
202 .push_opcode(opcodes::all::OP_CHECKSIG)
203 .into_script()
204 }
205
206 pub fn hash_and_sign(hash: sha256::Hash, pubkey: XOnlyPublicKey) -> ScriptBuf {
212 let hash_160 = ripemd160::Hash::hash(&hash[..]);
213
214 bitcoin::Script::builder()
215 .push_opcode(opcodes::all::OP_SIZE)
216 .push_int(32)
217 .push_opcode(opcodes::all::OP_EQUALVERIFY)
218 .push_opcode(opcodes::all::OP_HASH160)
219 .push_slice(hash_160.as_byte_array())
220 .push_opcode(opcodes::all::OP_EQUALVERIFY)
221 .push_x_only_key(&pubkey)
222 .push_opcode(opcodes::all::OP_CHECKSIG)
223 .into_script()
224 }
225
226 pub fn hash_and_sign_v0(hash: sha256::Hash, pubkey: XOnlyPublicKey) -> ScriptBuf {
232 let hash_160 = ripemd160::Hash::hash(&hash[..]);
233
234 bitcoin::Script::builder()
235 .push_opcode(opcodes::all::OP_HASH160)
236 .push_slice(hash_160.as_byte_array())
237 .push_opcode(opcodes::all::OP_EQUALVERIFY)
238 .push_x_only_key(&pubkey)
239 .push_opcode(opcodes::all::OP_CHECKSIG)
240 .into_script()
241 }
242
243 pub fn hash_delay_sign(
244 hash: sha256::Hash,
245 delay_blocks: BlockDelta,
246 pubkey: XOnlyPublicKey,
247 ) -> ScriptBuf {
248 let hash_160 = ripemd160::Hash::hash(&hash[..]);
249 let csv = bitcoin::Sequence::from_height(delay_blocks);
250
251 bitcoin::Script::builder()
252 .push_int(csv.to_consensus_u32().try_into().unwrap())
253 .push_opcode(opcodes::all::OP_CSV)
254 .push_opcode(opcodes::all::OP_DROP)
255 .push_opcode(opcodes::all::OP_SIZE)
256 .push_int(32)
257 .push_opcode(opcodes::all::OP_EQUALVERIFY)
258 .push_opcode(opcodes::all::OP_HASH160)
259 .push_slice(hash_160.as_byte_array())
260 .push_opcode(opcodes::all::OP_EQUALVERIFY)
261 .push_x_only_key(&pubkey)
262 .push_opcode(opcodes::all::OP_CHECKSIG)
263 .into_script()
264 }
265
266 pub fn hash_delay_sign_v0(
267 hash: sha256::Hash,
268 delay_blocks: BlockDelta,
269 pubkey: XOnlyPublicKey,
270 ) -> ScriptBuf {
271 let hash_160 = ripemd160::Hash::hash(&hash[..]);
272 let csv = bitcoin::Sequence::from_height(delay_blocks);
273
274 bitcoin::Script::builder()
275 .push_int(csv.to_consensus_u32().try_into().unwrap())
276 .push_opcode(opcodes::all::OP_CSV)
277 .push_opcode(opcodes::all::OP_DROP)
278 .push_opcode(opcodes::all::OP_HASH160)
279 .push_slice(hash_160.as_byte_array())
280 .push_opcode(opcodes::all::OP_EQUALVERIFY)
281 .push_x_only_key(&pubkey)
282 .push_opcode(opcodes::all::OP_CHECKSIG)
283 .into_script()
284 }
285
286 pub fn fill_taproot_sigs(tx: &mut Transaction, sigs: &[schnorr::Signature]) {
291 assert_eq!(tx.input.len(), sigs.len());
292 for (input, sig) in tx.input.iter_mut().zip(sigs.iter()) {
293 assert!(input.witness.is_empty());
294 input.witness.push(&sig[..]);
295 debug_assert_eq!(TAPROOT_KEYSPEND_WEIGHT.to_wu(), input.witness.size() as u64);
296 }
297 }
298
299 pub fn verify_partial_sig(
301 sighash: TapSighash,
302 tweak: TapTweakHash,
303 signer: (PublicKey, &musig::PublicNonce),
304 other: (PublicKey, &musig::PublicNonce),
305 partial_signature: &musig::PartialSignature,
306 ) -> bool {
307 let agg_nonce = musig::nonce_agg(&[&signer.1, &other.1]);
308 let agg_pk = musig::tweaked_key_agg([signer.0, other.0], tweak.to_byte_array()).0;
309
310 let session = musig::Session::new(&agg_pk, agg_nonce, &sighash.to_byte_array());
311 session.partial_verify(
312 &agg_pk, partial_signature, signer.1, musig::pubkey_to(signer.0),
313 )
314 }
315}