// TODO: This module needs cleanup related to PublicKey/SecretKey types. use std::{ cmp::max, collections::BTreeMap, convert::{From, TryFrom, TryInto}, fmt, ops::Add, str::FromStr, time::{Duration, Instant}, }; use anyhow::Context; use async_executor::Executor; use async_std::sync::{Arc, Mutex}; use async_trait::async_trait; use bdk::electrum_client::{ Client as ElectrumClient, ElectrumApi, GetBalanceRes, GetHistoryRes, HeaderNotification, }; use bitcoin::{ blockdata::{ script::{Builder, Script}, transaction::{OutPoint, SigHashType, Transaction, TxIn, TxOut}, }, consensus::encode::serialize_hex, hash_types::PubkeyHash as BtcPubKeyHash, network::constants::Network, util::{ address::Address, ecdsa::{PrivateKey as BtcPrivKey, PublicKey as BtcPubKey}, psbt::serialize::Serialize, }, }; use log::*; use secp256k1::{ constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE}, key::{PublicKey, SecretKey}, rand::rngs::OsRng, All, Message as BtcMessage, Secp256k1, }; use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion}; use crate::{ crypto::keypair::PublicKey as DrkPublicKey, serial::{deserialize, serialize, Decodable, Encodable}, util::{generate_id2, NetworkName}, Error, Result, }; // Swap out these types for any future non bitcoin-rs types pub type PubAddress = Address; pub type PubKey = BtcPubKey; pub type PrivKey = BtcPrivKey; const KEYPAIR_LENGTH: usize = SECRET_KEY_SIZE + PUBLIC_KEY_SIZE; #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct BlockHeight(u32); impl From for u32 { fn from(height: BlockHeight) -> Self { height.0 } } impl TryFrom for BlockHeight { type Error = BtcFailed; fn try_from(value: HeaderNotification) -> BtcResult { Ok(Self(value.height.try_into().context("Failed to fit usize into u32")?)) } } impl Add for BlockHeight { type Output = BlockHeight; fn add(self, rhs: u32) -> Self::Output { BlockHeight(self.0 + rhs) } } #[derive(Debug, Clone, Copy, PartialEq)] pub enum ExpiredTimelocks { None, Cancel, Punish, } #[derive(Clone, Debug, PartialEq)] pub struct Keypair { secret: SecretKey, public: PublicKey, context: Secp256k1, } impl Keypair { pub fn new() -> Self { let secp = Secp256k1::new(); let mut rng = OsRng::new().expect("OsRng"); let (secret, public) = secp.generate_keypair(&mut rng); Self { secret, public, context: secp } } pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] { let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; bytes[..SECRET_KEY_SIZE].copy_from_slice(self.secret.as_ref()); bytes[SECRET_KEY_SIZE..].copy_from_slice(&self.public.serialize()); bytes } pub fn from_bytes(bytes: &[u8]) -> BtcResult { if bytes.len() != KEYPAIR_LENGTH { return Err(BtcFailed::KeypairError("Not right size".to_string())) } let secp = Secp256k1::new(); let secret = SecretKey::from_slice(&bytes[..SECRET_KEY_SIZE])?; let public = PublicKey::from_slice(&bytes[SECRET_KEY_SIZE..])?; Ok(Keypair { secret, public, context: secp }) } fn secret(&self) -> SecretKey { self.secret } pub fn pubkey(&self) -> PublicKey { self.public } pub fn as_tuple(&self) -> (SecretKey, PublicKey) { (self.secret, self.public) } } impl Default for Keypair { fn default() -> Self { Self::new() } } #[derive(Clone)] pub struct Account { keypair: Arc, btc_privkey: BtcPrivKey, pub btc_pubkey: BtcPubKey, pub address: Address, pub script_pubkey: Script, pub network: Network, } impl Account { pub fn new(keypair: &Keypair, network: Network) -> Self { let (secret_key, _public_key) = keypair.as_tuple(); let btc_privkey = BtcPrivKey::new(secret_key, network); let btc_pubkey = btc_privkey.public_key(&keypair.context); let address = Account::derive_btc_address(btc_pubkey, network); let script_pubkey = address.script_pubkey(); Self { keypair: Arc::new(keypair.clone()), btc_privkey, btc_pubkey, address, script_pubkey, network, } } pub fn priv_from_secret(keypair: &Keypair, network: Network) -> BtcPrivKey { BtcPrivKey::new(keypair.secret(), network) } pub fn btcpub_from_keypair(keypair: &Keypair) -> BtcPubKey { BtcPubKey::new(keypair.public) } pub fn btc_privkey(&self) -> &BtcPrivKey { &self.btc_privkey } pub fn btc_pubkey(&self) -> &BtcPubKey { &self.btc_pubkey } pub fn btc_pubkey_hash(&self) -> BtcPubKeyHash { self.btc_pubkey.pubkey_hash() } pub fn derive_btc_script_pubkey(pubkey: PublicKey, network: Network) -> Script { let btc_pubkey = BtcPubKey::new(pubkey); let address = Address::p2pkh(&btc_pubkey, network); address.script_pubkey() } pub fn derive_btc_pubkey(pubkey: PublicKey) -> BtcPubKey { BtcPubKey::new(pubkey) } pub fn derive_btc_address(btc_pubkey: BtcPubKey, network: Network) -> Address { Address::p2pkh(&btc_pubkey, network) } pub fn derive_script(btc_pubkey_hash: BtcPubKeyHash) -> Script { Script::new_p2pkh(&btc_pubkey_hash) } } fn print_status_change( script: &Script, old: Option, new: ScriptStatus, ) -> ScriptStatus { match (old, new) { (None, new_status) => { debug!(target: "BTC BRIDGE", "Found relevant script: {:?}, Status: {:?}", script, new_status); } (Some(old_status), new_status) if old_status != new_status => { debug!(target: "BTC BRIDGE", "Script status changed: {:?}, to {} from {}", script, new_status, old_status); } _ => {} } new } fn sync_interval(avg_block_time: Duration) -> Duration { max(avg_block_time / 10, Duration::from_secs(1)) } pub struct Client { electrum: ElectrumClient, subscriptions: Vec