1
2pub extern crate bitcoin;
3
4#[macro_use] extern crate serde;
5#[macro_use] extern crate lazy_static;
6
7#[macro_use] 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 forfeit;
17pub mod lightning;
18pub mod mailbox;
19pub mod musig;
20pub mod offboard;
21pub mod rounds;
22pub mod tree;
23pub mod vtxo;
24pub mod integration;
25
26pub use crate::address::Address;
27pub use crate::encode::{ProtocolEncoding, WriteExt, ReadExt, ProtocolDecodingError};
28pub use crate::vtxo::{Vtxo, VtxoId, VtxoPolicy};
29
30#[cfg(test)]
31mod napkin;
32#[cfg(any(test, feature = "test-util"))]
33pub mod test;
34
35
36use std::time::Duration;
37
38use bitcoin::{Amount, FeeRate, Network};
39use bitcoin::secp256k1::{self, schnorr, PublicKey};
40
41use bitcoin_ext::BlockDelta;
42
43lazy_static! {
44 pub static ref SECP: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub struct ArkInfo {
50 pub network: Network,
52 pub server_pubkey: PublicKey,
54 pub mailbox_pubkey: PublicKey,
56 pub round_interval: Duration,
58 pub nb_round_nonces: usize,
60 pub vtxo_exit_delta: BlockDelta,
62 pub vtxo_expiry_delta: BlockDelta,
64 pub htlc_send_expiry_delta: BlockDelta,
66 pub htlc_expiry_delta: BlockDelta,
68 pub max_vtxo_amount: Option<Amount>,
70 pub required_board_confirmations: usize,
72 pub max_user_invoice_cltv_delta: u16,
75 pub min_board_amount: Amount,
77
78 pub offboard_feerate: FeeRate,
82
83 pub offboard_fixed_fee_vb: u64,
88
89 pub ln_receive_anti_dos_required: bool,
93}
94
95#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
97pub struct VtxoIdInput {
98 pub vtxo_id: VtxoId,
99 pub ownership_proof: schnorr::Signature,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
109pub struct VtxoRequest {
110 pub amount: Amount,
111 #[serde(with = "crate::encode::serde")]
112 pub policy: VtxoPolicy,
113}
114
115impl AsRef<VtxoRequest> for VtxoRequest {
116 fn as_ref(&self) -> &VtxoRequest {
117 self
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
123pub struct SignedVtxoRequest {
124 pub vtxo: VtxoRequest,
126 pub cosign_pubkey: PublicKey,
129 pub nonces: Vec<musig::PublicNonce>,
131}
132
133impl AsRef<VtxoRequest> for SignedVtxoRequest {
134 fn as_ref(&self) -> &VtxoRequest {
135 &self.vtxo
136 }
137}
138
139pub mod scripts {
140 use bitcoin::{opcodes, ScriptBuf, TapSighash, TapTweakHash, Transaction};
141 use bitcoin::hashes::{sha256, ripemd160, Hash};
142 use bitcoin::secp256k1::{schnorr, PublicKey, XOnlyPublicKey};
143
144 use bitcoin_ext::{BlockDelta, BlockHeight, TAPROOT_KEYSPEND_WEIGHT};
145
146 use crate::musig;
147
148 pub fn delayed_sign(delay_blocks: BlockDelta, pubkey: XOnlyPublicKey) -> ScriptBuf {
150 let csv = bitcoin::Sequence::from_height(delay_blocks);
151 bitcoin::Script::builder()
152 .push_int(csv.to_consensus_u32() as i64)
153 .push_opcode(opcodes::all::OP_CSV)
154 .push_opcode(opcodes::all::OP_DROP)
155 .push_x_only_key(&pubkey)
156 .push_opcode(opcodes::all::OP_CHECKSIG)
157 .into_script()
158 }
159
160 pub fn timelock_sign(timelock_height: BlockHeight, pubkey: XOnlyPublicKey) -> ScriptBuf {
162 let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
163 bitcoin::Script::builder()
164 .push_int(lt.to_consensus_u32() as i64)
165 .push_opcode(opcodes::all::OP_CLTV)
166 .push_opcode(opcodes::all::OP_DROP)
167 .push_x_only_key(&pubkey)
168 .push_opcode(opcodes::all::OP_CHECKSIG)
169 .into_script()
170 }
171
172 pub fn delay_timelock_sign(delay_blocks: BlockDelta, timelock_height: BlockHeight, pubkey: XOnlyPublicKey) -> ScriptBuf {
174 let csv = bitcoin::Sequence::from_height(delay_blocks);
175 let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
176 bitcoin::Script::builder()
177 .push_int(lt.to_consensus_u32().try_into().unwrap())
178 .push_opcode(opcodes::all::OP_CLTV)
179 .push_opcode(opcodes::all::OP_DROP)
180 .push_int(csv.to_consensus_u32().try_into().unwrap())
181 .push_opcode(opcodes::all::OP_CSV)
182 .push_opcode(opcodes::all::OP_DROP)
183 .push_x_only_key(&pubkey)
184 .push_opcode(opcodes::all::OP_CHECKSIG)
185 .into_script()
186 }
187
188 pub fn hash_and_sign(hash: sha256::Hash, pubkey: XOnlyPublicKey) -> ScriptBuf {
194 let hash_160 = ripemd160::Hash::hash(&hash[..]);
195
196 bitcoin::Script::builder()
197 .push_opcode(opcodes::all::OP_HASH160)
198 .push_slice(hash_160.as_byte_array())
199 .push_opcode(opcodes::all::OP_EQUALVERIFY)
200 .push_x_only_key(&pubkey)
201 .push_opcode(opcodes::all::OP_CHECKSIG)
202 .into_script()
203 }
204
205 pub fn hash_delay_sign(hash: sha256::Hash, delay_blocks: BlockDelta, pubkey: XOnlyPublicKey) -> ScriptBuf {
206 let hash_160 = ripemd160::Hash::hash(&hash[..]);
207 let csv = bitcoin::Sequence::from_height(delay_blocks);
208
209 bitcoin::Script::builder()
210 .push_int(csv.to_consensus_u32().try_into().unwrap())
211 .push_opcode(opcodes::all::OP_CSV)
212 .push_opcode(opcodes::all::OP_DROP)
213 .push_opcode(opcodes::all::OP_HASH160)
214 .push_slice(hash_160.as_byte_array())
215 .push_opcode(opcodes::all::OP_EQUALVERIFY)
216 .push_x_only_key(&pubkey)
217 .push_opcode(opcodes::all::OP_CHECKSIG)
218 .into_script()
219 }
220
221 pub fn fill_taproot_sigs(tx: &mut Transaction, sigs: &[schnorr::Signature]) {
226 assert_eq!(tx.input.len(), sigs.len());
227 for (input, sig) in tx.input.iter_mut().zip(sigs.iter()) {
228 assert!(input.witness.is_empty());
229 input.witness.push(&sig[..]);
230 debug_assert_eq!(TAPROOT_KEYSPEND_WEIGHT, input.witness.size());
231 }
232 }
233
234 pub fn verify_partial_sig(
236 sighash: TapSighash,
237 tweak: TapTweakHash,
238 signer: (PublicKey, &musig::PublicNonce),
239 other: (PublicKey, &musig::PublicNonce),
240 partial_signature: &musig::PartialSignature,
241 ) -> bool {
242 let agg_nonce = musig::nonce_agg(&[&signer.1, &other.1]);
243 let agg_pk = musig::tweaked_key_agg([signer.0, other.0], tweak.to_byte_array()).0;
244
245 let session = musig::Session::new(&agg_pk, agg_nonce, &sighash.to_byte_array());
246 session.partial_verify(
247 &agg_pk, partial_signature, signer.1, musig::pubkey_to(signer.0),
248 )
249 }
250}