pub extern crate bitcoin;
#[macro_use] extern crate serde;
#[macro_use] extern crate lazy_static;
#[macro_use] pub mod util;
pub mod address;
pub mod arkoor;
pub mod attestations;
pub mod board;
pub mod connectors;
pub mod encode;
pub mod error;
pub mod fees;
pub mod forfeit;
pub mod lightning;
pub mod mailbox;
pub mod musig;
pub mod offboard;
pub mod rounds;
pub mod time;
pub mod tree;
pub mod vtxo;
pub mod integration;
pub use crate::address::Address;
pub use crate::encode::{ProtocolEncoding, WriteExt, ReadExt, ProtocolDecodingError};
pub use crate::vtxo::{Vtxo, VtxoId, VtxoPolicy, ServerVtxoPolicy, ServerVtxo};
#[cfg(test)]
mod napkin;
#[cfg(any(test, feature = "test-util"))]
pub mod test_util;
use std::time::Duration;
use bitcoin::{Amount, FeeRate, Network};
use bitcoin::secp256k1::{self, PublicKey};
use bitcoin_ext::BlockDelta;
use crate::fees::FeeSchedule;
lazy_static! {
pub static ref SECP: secp256k1::Secp256k1<secp256k1::All> = secp256k1::Secp256k1::new();
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArkInfo {
pub network: Network,
pub server_pubkey: PublicKey,
pub mailbox_pubkey: PublicKey,
pub round_interval: Duration,
pub nb_round_nonces: usize,
pub vtxo_exit_delta: BlockDelta,
pub vtxo_expiry_delta: BlockDelta,
pub htlc_send_expiry_delta: BlockDelta,
pub htlc_expiry_delta: BlockDelta,
pub max_vtxo_amount: Option<Amount>,
pub required_board_confirmations: usize,
pub max_user_invoice_cltv_delta: u16,
pub min_board_amount: Amount,
pub offboard_feerate: FeeRate,
pub ln_receive_anti_dos_required: bool,
pub fees: FeeSchedule,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct VtxoRequest {
pub amount: Amount,
#[serde(with = "crate::encode::serde")]
pub policy: VtxoPolicy,
}
impl AsRef<VtxoRequest> for VtxoRequest {
fn as_ref(&self) -> &VtxoRequest {
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub struct SignedVtxoRequest {
pub vtxo: VtxoRequest,
pub cosign_pubkey: PublicKey,
pub nonces: Vec<musig::PublicNonce>,
}
impl AsRef<VtxoRequest> for SignedVtxoRequest {
fn as_ref(&self) -> &VtxoRequest {
&self.vtxo
}
}
pub mod scripts {
use bitcoin::{opcodes, ScriptBuf, TapSighash, TapTweakHash, Transaction};
use bitcoin::hashes::{sha256, ripemd160, Hash};
use bitcoin::secp256k1::{schnorr, PublicKey, XOnlyPublicKey};
use bitcoin_ext::{BlockDelta, BlockHeight, TAPROOT_KEYSPEND_WEIGHT};
use crate::musig;
pub fn delayed_sign(delay_blocks: BlockDelta, pubkey: XOnlyPublicKey) -> ScriptBuf {
let csv = bitcoin::Sequence::from_height(delay_blocks);
bitcoin::Script::builder()
.push_int(csv.to_consensus_u32() as i64)
.push_opcode(opcodes::all::OP_CSV)
.push_opcode(opcodes::all::OP_DROP)
.push_x_only_key(&pubkey)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
pub fn timelock_sign(timelock_height: BlockHeight, pubkey: XOnlyPublicKey) -> ScriptBuf {
let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
bitcoin::Script::builder()
.push_int(lt.to_consensus_u32() as i64)
.push_opcode(opcodes::all::OP_CLTV)
.push_opcode(opcodes::all::OP_DROP)
.push_x_only_key(&pubkey)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
pub fn delay_timelock_sign(
delay_blocks: BlockDelta,
timelock_height: BlockHeight,
pubkey: XOnlyPublicKey,
) -> ScriptBuf {
let csv = bitcoin::Sequence::from_height(delay_blocks);
let lt = bitcoin::absolute::LockTime::from_height(timelock_height).unwrap();
bitcoin::Script::builder()
.push_int(lt.to_consensus_u32().try_into().unwrap())
.push_opcode(opcodes::all::OP_CLTV)
.push_opcode(opcodes::all::OP_DROP)
.push_int(csv.to_consensus_u32().try_into().unwrap())
.push_opcode(opcodes::all::OP_CSV)
.push_opcode(opcodes::all::OP_DROP)
.push_x_only_key(&pubkey)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
pub fn hash_and_sign(hash: sha256::Hash, pubkey: XOnlyPublicKey) -> ScriptBuf {
let hash_160 = ripemd160::Hash::hash(&hash[..]);
bitcoin::Script::builder()
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(hash_160.as_byte_array())
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_x_only_key(&pubkey)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
pub fn hash_delay_sign(
hash: sha256::Hash,
delay_blocks: BlockDelta,
pubkey: XOnlyPublicKey,
) -> ScriptBuf {
let hash_160 = ripemd160::Hash::hash(&hash[..]);
let csv = bitcoin::Sequence::from_height(delay_blocks);
bitcoin::Script::builder()
.push_int(csv.to_consensus_u32().try_into().unwrap())
.push_opcode(opcodes::all::OP_CSV)
.push_opcode(opcodes::all::OP_DROP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(hash_160.as_byte_array())
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_x_only_key(&pubkey)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
pub fn fill_taproot_sigs(tx: &mut Transaction, sigs: &[schnorr::Signature]) {
assert_eq!(tx.input.len(), sigs.len());
for (input, sig) in tx.input.iter_mut().zip(sigs.iter()) {
assert!(input.witness.is_empty());
input.witness.push(&sig[..]);
debug_assert_eq!(TAPROOT_KEYSPEND_WEIGHT, input.witness.size());
}
}
pub fn verify_partial_sig(
sighash: TapSighash,
tweak: TapTweakHash,
signer: (PublicKey, &musig::PublicNonce),
other: (PublicKey, &musig::PublicNonce),
partial_signature: &musig::PartialSignature,
) -> bool {
let agg_nonce = musig::nonce_agg(&[&signer.1, &other.1]);
let agg_pk = musig::tweaked_key_agg([signer.0, other.0], tweak.to_byte_array()).0;
let session = musig::Session::new(&agg_pk, agg_nonce, &sighash.to_byte_array());
session.partial_verify(
&agg_pk, partial_signature, signer.1, musig::pubkey_to(signer.0),
)
}
}