use std::sync::Arc;
use crate::builder::script::{
BaseDepositScript, Multisig, ReplacementDepositScript, SpendableScript, TimelockScript,
};
use crate::config::protocol::ProtocolParamset;
use crate::errors::BridgeError;
use crate::musig2::AggregateFromPublicKeys;
use crate::operator::RoundIndex;
use crate::EVMAddress;
use bitcoin::address::NetworkUnchecked;
use bitcoin::secp256k1::PublicKey;
use bitcoin::{Address, OutPoint, Txid, XOnlyPublicKey};
use eyre::Context;
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Ord, PartialOrd,
)]
pub struct KickoffData {
pub operator_xonly_pk: XOnlyPublicKey,
pub round_idx: RoundIndex,
pub kickoff_idx: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Eq)]
pub struct DepositData {
pub nofn_xonly_pk: Option<XOnlyPublicKey>,
pub deposit: DepositInfo,
pub actors: Actors,
pub security_council: SecurityCouncil,
}
impl PartialEq for DepositData {
fn eq(&self, other: &Self) -> bool {
self.security_council == other.security_council
&& self.deposit.deposit_outpoint == other.deposit.deposit_outpoint
&& self.get_operators() == other.get_operators()
&& self.get_verifiers() == other.get_verifiers()
&& self.get_watchtowers() == other.get_watchtowers()
&& self.deposit.deposit_type == other.deposit.deposit_type
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct DepositInfo {
pub deposit_outpoint: OutPoint,
pub deposit_type: DepositType,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub enum DepositType {
BaseDeposit(BaseDepositData),
ReplacementDeposit(ReplacementDepositData),
}
impl DepositData {
pub fn get_deposit_outpoint(&self) -> OutPoint {
self.deposit.deposit_outpoint
}
pub fn get_nofn_xonly_pk(&mut self) -> Result<XOnlyPublicKey, BridgeError> {
if let Some(pk) = self.nofn_xonly_pk {
return Ok(pk);
}
let verifiers = self.get_verifiers();
let nofn_xonly_pk = bitcoin::XOnlyPublicKey::from_musig2_pks(verifiers, None)?;
self.nofn_xonly_pk = Some(nofn_xonly_pk);
Ok(nofn_xonly_pk)
}
pub fn get_num_verifiers(&self) -> usize {
self.actors.verifiers.len()
}
pub fn get_num_watchtowers(&self) -> usize {
self.get_num_verifiers() + self.actors.watchtowers.len()
}
pub fn get_verifier_index(&self, public_key: &PublicKey) -> Result<usize, eyre::Report> {
self.get_verifiers()
.iter()
.position(|pk| pk == public_key)
.ok_or_else(|| eyre::eyre!("Verifier with public key {} not found", public_key))
}
pub fn get_watchtower_index(&self, xonly_pk: &XOnlyPublicKey) -> Result<usize, eyre::Report> {
self.get_watchtowers()
.iter()
.position(|pk| pk == xonly_pk)
.ok_or_else(|| eyre::eyre!("Watchtower with xonly key {} not found", xonly_pk))
}
pub fn get_operator_index(&self, xonly_pk: XOnlyPublicKey) -> Result<usize, eyre::Report> {
self.get_operators()
.iter()
.position(|pk| pk == &xonly_pk)
.ok_or_else(|| eyre::eyre!("Operator with xonly key {} not found", xonly_pk))
}
pub fn get_verifiers(&self) -> Vec<PublicKey> {
let mut verifiers = self.actors.verifiers.clone();
verifiers.sort();
verifiers
}
pub fn get_watchtowers(&self) -> Vec<XOnlyPublicKey> {
let mut watchtowers = self
.actors
.verifiers
.iter()
.map(|pk| pk.x_only_public_key().0)
.collect::<Vec<_>>();
watchtowers.extend(self.actors.watchtowers.iter());
watchtowers.sort();
watchtowers
}
pub fn get_operators(&self) -> Vec<XOnlyPublicKey> {
let mut operators = self.actors.operators.clone();
operators.sort();
operators
}
pub fn get_num_operators(&self) -> usize {
self.actors.operators.len()
}
pub fn get_deposit_scripts(
&mut self,
paramset: &'static ProtocolParamset,
) -> Result<Vec<Arc<dyn SpendableScript>>, BridgeError> {
let nofn_xonly_pk = self.get_nofn_xonly_pk()?;
match &mut self.deposit.deposit_type {
DepositType::BaseDeposit(original_deposit_data) => {
let deposit_script = Arc::new(BaseDepositScript::new(
nofn_xonly_pk,
original_deposit_data.evm_address,
));
let recovery_script_pubkey = original_deposit_data
.recovery_taproot_address
.clone()
.assume_checked()
.script_pubkey();
let recovery_extracted_xonly_pk =
XOnlyPublicKey::from_slice(&recovery_script_pubkey.as_bytes()[2..34])
.wrap_err(
"Failed to extract xonly public key from recovery script pubkey",
)?;
let script_timelock = Arc::new(TimelockScript::new(
Some(recovery_extracted_xonly_pk),
paramset.user_takes_after,
));
Ok(vec![deposit_script, script_timelock])
}
DepositType::ReplacementDeposit(replacement_deposit_data) => {
let deposit_script: Arc<dyn SpendableScript> =
Arc::new(ReplacementDepositScript::new(
nofn_xonly_pk,
replacement_deposit_data.old_move_txid,
));
let security_council_script: Arc<dyn SpendableScript> = Arc::new(
Multisig::from_security_council(self.security_council.clone()),
);
Ok(vec![deposit_script, security_council_script])
}
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct Actors {
pub verifiers: Vec<PublicKey>,
pub watchtowers: Vec<XOnlyPublicKey>,
pub operators: Vec<XOnlyPublicKey>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SecurityCouncil {
pub pks: Vec<XOnlyPublicKey>,
pub threshold: u32,
}
impl std::str::FromStr for SecurityCouncil {
type Err = eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let threshold_str = parts
.next()
.ok_or_else(|| eyre::eyre!("Missing threshold"))?;
let pks_str = parts
.next()
.ok_or_else(|| eyre::eyre!("Missing public keys"))?;
if parts.next().is_some() {
return Err(eyre::eyre!("Too many parts in security council string"));
}
let threshold = threshold_str
.parse::<u32>()
.map_err(|e| eyre::eyre!("Invalid threshold: {}", e))?;
let pks: Result<Vec<XOnlyPublicKey>, _> = pks_str
.split(',')
.map(|pk_str| {
let bytes = hex::decode(pk_str)
.map_err(|e| eyre::eyre!("Invalid hex in public key: {}", e))?;
XOnlyPublicKey::from_slice(&bytes)
.map_err(|e| eyre::eyre!("Invalid public key: {}", e))
})
.collect();
let pks = pks?;
if pks.is_empty() {
return Err(eyre::eyre!("No public keys provided"));
}
if threshold > pks.len() as u32 {
return Err(eyre::eyre!(
"Threshold cannot be greater than number of public keys"
));
}
Ok(SecurityCouncil { pks, threshold })
}
}
impl serde::Serialize for SecurityCouncil {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> serde::Deserialize<'de> for SecurityCouncil {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
impl std::fmt::Display for SecurityCouncil {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:", self.threshold)?;
let pks_str = self
.pks
.iter()
.map(|pk| hex::encode(pk.serialize()))
.collect::<Vec<_>>()
.join(",");
write!(f, "{}", pks_str)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct BaseDepositData {
pub evm_address: EVMAddress,
pub recovery_taproot_address: bitcoin::Address<NetworkUnchecked>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ReplacementDepositData {
pub old_move_txid: Txid,
}
#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
pub struct OperatorData {
pub xonly_pk: XOnlyPublicKey,
pub reimburse_addr: Address,
pub collateral_funding_outpoint: OutPoint,
}
impl<'de> serde::Deserialize<'de> for OperatorData {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct OperatorDataHelper {
xonly_pk: XOnlyPublicKey,
reimburse_addr: Address<NetworkUnchecked>,
collateral_funding_outpoint: OutPoint,
}
let helper = OperatorDataHelper::deserialize(deserializer)?;
Ok(OperatorData {
xonly_pk: helper.xonly_pk,
reimburse_addr: helper.reimburse_addr.assume_checked(),
collateral_funding_outpoint: helper.collateral_funding_outpoint,
})
}
}