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 board;
12pub mod challenges;
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 time;
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, schnorr, 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_expiry_delta: 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 pub offboard_feerate: FeeRate,
86
87 pub ln_receive_anti_dos_required: bool,
91
92 pub fees: FeeSchedule,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
98pub struct VtxoIdInput {
99 pub vtxo_id: VtxoId,
100 pub ownership_proof: schnorr::Signature,
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
110pub struct VtxoRequest {
111 pub amount: Amount,
112 #[serde(with = "crate::encode::serde")]
113 pub policy: VtxoPolicy,
114}
115
116impl AsRef<VtxoRequest> for VtxoRequest {
117 fn as_ref(&self) -> &VtxoRequest {
118 self
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
124pub struct SignedVtxoRequest {
125 pub vtxo: VtxoRequest,
127 pub cosign_pubkey: PublicKey,
130 pub nonces: Vec<musig::PublicNonce>,
132}
133
134impl AsRef<VtxoRequest> for SignedVtxoRequest {
135 fn as_ref(&self) -> &VtxoRequest {
136 &self.vtxo
137 }
138}
139
140pub mod scripts {
141 use bitcoin::{opcodes, ScriptBuf, TapSighash, TapTweakHash, Transaction};
142 use bitcoin::hashes::{sha256, ripemd160, Hash};
143 use bitcoin::secp256k1::{schnorr, PublicKey, XOnlyPublicKey};
144
145 use bitcoin_ext::{BlockDelta, BlockHeight, TAPROOT_KEYSPEND_WEIGHT};
146
147 use crate::musig;
148
149 pub fn delayed_sign(delay_blocks: BlockDelta, pubkey: XOnlyPublicKey) -> ScriptBuf {
151 let csv = bitcoin::Sequence::from_height(delay_blocks);
152 bitcoin::Script::builder()
153 .push_int(csv.to_consensus_u32() as i64)
154 .push_opcode(opcodes::all::OP_CSV)
155 .push_opcode(opcodes::all::OP_DROP)
156 .push_x_only_key(&pubkey)
157 .push_opcode(opcodes::all::OP_CHECKSIG)
158 .into_script()
159 }
160
161 pub fn timelock_sign(timelock_height: BlockHeight, pubkey: XOnlyPublicKey) -> ScriptBuf {
163 let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
164 bitcoin::Script::builder()
165 .push_int(lt.to_consensus_u32() as i64)
166 .push_opcode(opcodes::all::OP_CLTV)
167 .push_opcode(opcodes::all::OP_DROP)
168 .push_x_only_key(&pubkey)
169 .push_opcode(opcodes::all::OP_CHECKSIG)
170 .into_script()
171 }
172
173 pub fn delay_timelock_sign(
175 delay_blocks: BlockDelta,
176 timelock_height: BlockHeight,
177 pubkey: XOnlyPublicKey,
178 ) -> ScriptBuf {
179 let csv = bitcoin::Sequence::from_height(delay_blocks);
180 let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
181 bitcoin::Script::builder()
182 .push_int(lt.to_consensus_u32().try_into().unwrap())
183 .push_opcode(opcodes::all::OP_CLTV)
184 .push_opcode(opcodes::all::OP_DROP)
185 .push_int(csv.to_consensus_u32().try_into().unwrap())
186 .push_opcode(opcodes::all::OP_CSV)
187 .push_opcode(opcodes::all::OP_DROP)
188 .push_x_only_key(&pubkey)
189 .push_opcode(opcodes::all::OP_CHECKSIG)
190 .into_script()
191 }
192
193 pub fn hash_and_sign(hash: sha256::Hash, pubkey: XOnlyPublicKey) -> ScriptBuf {
199 let hash_160 = ripemd160::Hash::hash(&hash[..]);
200
201 bitcoin::Script::builder()
202 .push_opcode(opcodes::all::OP_HASH160)
203 .push_slice(hash_160.as_byte_array())
204 .push_opcode(opcodes::all::OP_EQUALVERIFY)
205 .push_x_only_key(&pubkey)
206 .push_opcode(opcodes::all::OP_CHECKSIG)
207 .into_script()
208 }
209
210 pub fn hash_delay_sign(
211 hash: sha256::Hash,
212 delay_blocks: BlockDelta,
213 pubkey: XOnlyPublicKey,
214 ) -> ScriptBuf {
215 let hash_160 = ripemd160::Hash::hash(&hash[..]);
216 let csv = bitcoin::Sequence::from_height(delay_blocks);
217
218 bitcoin::Script::builder()
219 .push_int(csv.to_consensus_u32().try_into().unwrap())
220 .push_opcode(opcodes::all::OP_CSV)
221 .push_opcode(opcodes::all::OP_DROP)
222 .push_opcode(opcodes::all::OP_HASH160)
223 .push_slice(hash_160.as_byte_array())
224 .push_opcode(opcodes::all::OP_EQUALVERIFY)
225 .push_x_only_key(&pubkey)
226 .push_opcode(opcodes::all::OP_CHECKSIG)
227 .into_script()
228 }
229
230 pub fn fill_taproot_sigs(tx: &mut Transaction, sigs: &[schnorr::Signature]) {
235 assert_eq!(tx.input.len(), sigs.len());
236 for (input, sig) in tx.input.iter_mut().zip(sigs.iter()) {
237 assert!(input.witness.is_empty());
238 input.witness.push(&sig[..]);
239 debug_assert_eq!(TAPROOT_KEYSPEND_WEIGHT, input.witness.size());
240 }
241 }
242
243 pub fn verify_partial_sig(
245 sighash: TapSighash,
246 tweak: TapTweakHash,
247 signer: (PublicKey, &musig::PublicNonce),
248 other: (PublicKey, &musig::PublicNonce),
249 partial_signature: &musig::PartialSignature,
250 ) -> bool {
251 let agg_nonce = musig::nonce_agg(&[&signer.1, &other.1]);
252 let agg_pk = musig::tweaked_key_agg([signer.0, other.0], tweak.to_byte_array()).0;
253
254 let session = musig::Session::new(&agg_pk, agg_nonce, &sighash.to_byte_array());
255 session.partial_verify(
256 &agg_pk, partial_signature, signer.1, musig::pubkey_to(signer.0),
257 )
258 }
259}