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 tree;
24pub mod vtxo;
25pub mod integration;
26
27pub use crate::address::Address;
28pub use crate::encode::{ProtocolEncoding, WriteExt, ReadExt, ProtocolDecodingError};
29pub use crate::vtxo::{Vtxo, VtxoId, VtxoPolicy, ServerVtxoPolicy, ServerVtxo};
30
31#[cfg(test)]
32mod napkin;
33#[cfg(any(test, feature = "test-util"))]
34pub mod test_util;
35
36
37use std::time::Duration;
38
39use bitcoin::{Amount, FeeRate, Network};
40use bitcoin::secp256k1::{self, PublicKey};
41
42use bitcoin_ext::BlockDelta;
43
44use crate::fees::FeeSchedule;
45
46lazy_static! {
47 pub static ref SECP: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct ArkInfo {
53 pub network: Network,
55 pub server_pubkey: PublicKey,
57 pub mailbox_pubkey: PublicKey,
59 pub round_interval: Duration,
61 pub nb_round_nonces: usize,
63 pub vtxo_exit_delta: BlockDelta,
65 pub vtxo_expiry_delta: BlockDelta,
67 pub htlc_send_expiry_delta: BlockDelta,
69 pub htlc_expiry_delta: BlockDelta,
71 pub max_vtxo_amount: Option<Amount>,
73 pub required_board_confirmations: usize,
75 pub max_user_invoice_cltv_delta: u16,
78 pub min_board_amount: Amount,
80
81 #[deprecated(since = "0.1.5", note = "use ServerConnection::offboard_feerate instead")]
89 pub offboard_feerate: FeeRate,
90
91 pub max_offboard_inputs: usize,
93
94 pub ln_receive_anti_dos_required: bool,
98
99 pub fees: FeeSchedule,
101
102 pub max_vtxo_exit_depth: u16,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
111pub struct VtxoRequest {
112 pub amount: Amount,
113 #[serde(with = "crate::encode::serde")]
114 pub policy: VtxoPolicy,
115}
116
117impl AsRef<VtxoRequest> for VtxoRequest {
118 fn as_ref(&self) -> &VtxoRequest {
119 self
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
125pub struct SignedVtxoRequest {
126 pub vtxo: VtxoRequest,
128 pub cosign_pubkey: PublicKey,
131 pub nonces: Vec<musig::PublicNonce>,
133}
134
135impl AsRef<VtxoRequest> for SignedVtxoRequest {
136 fn as_ref(&self) -> &VtxoRequest {
137 &self.vtxo
138 }
139}
140
141pub mod scripts {
142 use bitcoin::{opcodes, ScriptBuf, TapSighash, TapTweakHash, Transaction};
143 use bitcoin::hashes::{sha256, ripemd160, Hash};
144 use bitcoin::secp256k1::{schnorr, PublicKey, XOnlyPublicKey};
145
146 use bitcoin_ext::{BlockDelta, BlockHeight, TAPROOT_KEYSPEND_WEIGHT};
147
148 use crate::musig;
149
150 pub fn delayed_sign(delay_blocks: BlockDelta, pubkey: XOnlyPublicKey) -> ScriptBuf {
152 let csv = bitcoin::Sequence::from_height(delay_blocks);
153 bitcoin::Script::builder()
154 .push_int(csv.to_consensus_u32() as i64)
155 .push_opcode(opcodes::all::OP_CSV)
156 .push_opcode(opcodes::all::OP_DROP)
157 .push_x_only_key(&pubkey)
158 .push_opcode(opcodes::all::OP_CHECKSIG)
159 .into_script()
160 }
161
162 pub fn timelock_sign(timelock_height: BlockHeight, pubkey: XOnlyPublicKey) -> ScriptBuf {
164 let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
165 bitcoin::Script::builder()
166 .push_int(lt.to_consensus_u32() as i64)
167 .push_opcode(opcodes::all::OP_CLTV)
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 delay_timelock_sign(
176 delay_blocks: BlockDelta,
177 timelock_height: BlockHeight,
178 pubkey: XOnlyPublicKey,
179 ) -> ScriptBuf {
180 let csv = bitcoin::Sequence::from_height(delay_blocks);
181 let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
182 bitcoin::Script::builder()
183 .push_int(lt.to_consensus_u32().try_into().unwrap())
184 .push_opcode(opcodes::all::OP_CLTV)
185 .push_opcode(opcodes::all::OP_DROP)
186 .push_int(csv.to_consensus_u32().try_into().unwrap())
187 .push_opcode(opcodes::all::OP_CSV)
188 .push_opcode(opcodes::all::OP_DROP)
189 .push_x_only_key(&pubkey)
190 .push_opcode(opcodes::all::OP_CHECKSIG)
191 .into_script()
192 }
193
194 pub fn hash_and_sign(hash: sha256::Hash, pubkey: XOnlyPublicKey) -> ScriptBuf {
200 let hash_160 = ripemd160::Hash::hash(&hash[..]);
201
202 bitcoin::Script::builder()
203 .push_opcode(opcodes::all::OP_HASH160)
204 .push_slice(hash_160.as_byte_array())
205 .push_opcode(opcodes::all::OP_EQUALVERIFY)
206 .push_x_only_key(&pubkey)
207 .push_opcode(opcodes::all::OP_CHECKSIG)
208 .into_script()
209 }
210
211 pub fn hash_delay_sign(
212 hash: sha256::Hash,
213 delay_blocks: BlockDelta,
214 pubkey: XOnlyPublicKey,
215 ) -> ScriptBuf {
216 let hash_160 = ripemd160::Hash::hash(&hash[..]);
217 let csv = bitcoin::Sequence::from_height(delay_blocks);
218
219 bitcoin::Script::builder()
220 .push_int(csv.to_consensus_u32().try_into().unwrap())
221 .push_opcode(opcodes::all::OP_CSV)
222 .push_opcode(opcodes::all::OP_DROP)
223 .push_opcode(opcodes::all::OP_HASH160)
224 .push_slice(hash_160.as_byte_array())
225 .push_opcode(opcodes::all::OP_EQUALVERIFY)
226 .push_x_only_key(&pubkey)
227 .push_opcode(opcodes::all::OP_CHECKSIG)
228 .into_script()
229 }
230
231 pub fn fill_taproot_sigs(tx: &mut Transaction, sigs: &[schnorr::Signature]) {
236 assert_eq!(tx.input.len(), sigs.len());
237 for (input, sig) in tx.input.iter_mut().zip(sigs.iter()) {
238 assert!(input.witness.is_empty());
239 input.witness.push(&sig[..]);
240 debug_assert_eq!(TAPROOT_KEYSPEND_WEIGHT.to_wu(), input.witness.size() as u64);
241 }
242 }
243
244 pub fn verify_partial_sig(
246 sighash: TapSighash,
247 tweak: TapTweakHash,
248 signer: (PublicKey, &musig::PublicNonce),
249 other: (PublicKey, &musig::PublicNonce),
250 partial_signature: &musig::PartialSignature,
251 ) -> bool {
252 let agg_nonce = musig::nonce_agg(&[&signer.1, &other.1]);
253 let agg_pk = musig::tweaked_key_agg([signer.0, other.0], tweak.to_byte_array()).0;
254
255 let session = musig::Session::new(&agg_pk, agg_nonce, &sighash.to_byte_array());
256 session.partial_verify(
257 &agg_pk, partial_signature, signer.1, musig::pubkey_to(signer.0),
258 )
259 }
260}